From 8de993b2f71742e87b09d823ab83b994ec462120 Mon Sep 17 00:00:00 2001 From: Lopes Date: Mon, 3 Nov 2025 18:08:26 +0100 Subject: [PATCH 01/11] Resolving part of the issue #272 --- 03_functions.ipynb | 10 +- 04_input_output.ipynb | 559 ++++++++++++++++++++++----- 05_object_oriented_programming.ipynb | 2 +- tutorial/quiz/input_output.py | 8 +- tutorial/tests/data/example1.csv | 11 + tutorial/tests/data/me.txt | 1 + tutorial/tests/data/numbers.txt | 10 + tutorial/tests/data/output.txt | Bin 0 -> 10 bytes tutorial/tests/data/output1.txt | Bin 0 -> 2048 bytes tutorial/tests/test_03_functions.py | 28 +- 10 files changed, 512 insertions(+), 117 deletions(-) create mode 100644 tutorial/tests/data/example1.csv create mode 100644 tutorial/tests/data/me.txt create mode 100644 tutorial/tests/data/numbers.txt create mode 100644 tutorial/tests/data/output.txt create mode 100644 tutorial/tests/data/output1.txt diff --git a/03_functions.ipynb b/03_functions.ipynb index ef3f619f..a549e075 100644 --- a/03_functions.ipynb +++ b/03_functions.ipynb @@ -489,7 +489,7 @@ "\n", "*Hint:* you can use the [built-in function](https://docs.python.org/3/library/functions.html#round) `round(number, ndigits)` function to round the floating point `number` to a given number expressed by `ndigits`.\n", "\n", - "Example: `length=2`, `width=3` should return `\"6.00 cm^2\"`\n", + "Example: `length=1.5`, `width=2.57` should return `\"3.85 cm^2\"`\n", "\n", "
\n", "

Note

\n", @@ -515,7 +515,7 @@ "\n", " Returns:\n", " - A string representing the area with 2 decimal places,\n", - " followed by \"cm^2\" (e.g., \"6.00 cm^2\")\n", + " followed by \"cm^2\" (e.g., \"3.85 cm^2\")\n", " \"\"\"\n", " return" ] @@ -528,12 +528,14 @@ "### Part 2\n", "\n", "Extend the previous function, now called `solution_calculate_metric_area`, by adding a `unit` parameter that can be either \"cm\" or \"m\".\n", + "The default `length` and `width` unit is centimeters (\"cm\").\n", + "\n", "If the unit is \"m\", convert the measurements to centimeters before calculating the area.\n", "The result should still be in cm^2.\n", "\n", "*Hint:* remember the [built-in function](https://docs.python.org/3/library/functions.html#round) `round(number, ndigits)`.\n", "\n", - "Example: `length=2`, `width=3`, `unit=\"m\"` should return `\"60000.00 cm^2\"` (because 2m × 3m = 6m² = 60000cm²)\n", + "Example: `length=2.0`, `width=3.0`, `unit=\"m\"` should return `\"60000.0 cm^2\"` (because 2m × 3m = 6m² = 60000cm²)\n", "\n", "
\n", "

Note

\n", @@ -573,7 +575,7 @@ "### Part 3\n", "\n", "Extend the previous function, now called `solution_calculate_area`, to support the following units: cm, m, mm, yd, and ft.\n", - "The calculated area should always be in centimeters, so you need to take care of the appropriate conversions (when needed).\n", + "The calculated area should always be in cm^2, so you need to take care of the appropriate conversions (when needed).\n", "Keep the same string format for the result.\n", "\n", "Use the following conversion factors:\n", diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 7a4c3153..4ce37f05 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -40,11 +40,11 @@ " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", " - [Quiz on CSV](#Quiz-on-CSV)\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-🌶️🌶️🌶️)" + " - [Exercise 1: CSV to dictionary (easy)](#Exercise-1:-CSV-to-dictionary-(easy))\n", + " - [Exercise 2: Counting words (easy)](#Exercise-2:-Counting-words-(easy))\n", + " - [Exercise 3: Letter statistics (medium)](#Exercise-3:-Letter-statistics-(medium))\n", + " - [Exercise 4: Translating words (medium)](#Exercise-4:-Translating-words-(medium))\n", + " - [Exercise 5: Binary format (hard)](#Exercise-5:-Binary-format-(hard))" ] }, { @@ -75,13 +75,7 @@ "- connecting to databases or other network services\n", "\n", "\n", - "The majority of these operations are covered by the Python standard library. We are going to see how to use them in this chapter.\n", - "\n", - "
\n", - "

Note

\n", - "In reference to the chapter on functional programming, it is interesting to note that these functions perform side-effects. Therefore, any code containing these operations is no longer pure and is not referentially transparent. The same function can return different values for the same argument if called multiple times, and the function can have long-distance effects. That means they can modify the program state elsewhere, leading to unexpected results.

\n", - "Therefore, we suggest separating input and output from the other computations in your program. For example, if you have a complex calculation requiring several user inputs at several stages of the process, consider writing a function that only performs the calculation given all inputs and then requires all inputs separately, for example, through a single file. This makes your code easier to debug, test and understand.\n", - "
" + "The majority of these operations are covered by the Python standard library. We are going to see how to use them in this chapter." ] }, { @@ -108,8 +102,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "It is also possible to print any other python object using `print`. \n", - "In that case, the `__str__` **magic method** on that object's class is [called](https://docs.python.org/3/reference/datamodel.html#object.__str__)." + "It is also possible to print any other python object using `print`." ] }, { @@ -154,7 +147,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", @@ -245,7 +240,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%ipytest debug\n", + "%%ipytest\n", "\n", "def solution_print_odd(n: int) -> None: \n", " \"\"\"Prints all odd numbers from 1 to n\n", @@ -255,8 +250,7 @@ "\n", " Returns:\n", " - None (prints to console)\n", - " \"\"\"\n", - " pass" + " \"\"\"" ] }, { @@ -287,7 +281,6 @@ " Returns:\n", " - None (prints to console)\n", " \"\"\"\n", - " pass\n", "\n", "solution_print_salutation()" ] @@ -495,9 +488,25 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b5d9190bff57413c8c14c640aa74557a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Paths(children=(Question(children=(HTML(value='Q1: What does the operator / do w…" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.Paths()" @@ -513,9 +522,34 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
🚫 OpenAI API key is not provided.
API key is missing.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
🔄 IPytest extension (re)loaded.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -535,9 +569,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5a82cdcf4ce1468d8718502eafc1c89a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Can you read from a file before callin…" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.ReadFiles()" @@ -739,9 +873,34 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
🚫 OpenAI API key is not provided.
API key is missing.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
🔄 IPytest extension (re)loaded.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -756,9 +915,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "658982e189394032bd022f74294fd393", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: What does w in the secon…" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.WriteFiles()" @@ -867,9 +1057,34 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
🚫 OpenAI API key is not provided.
API key is missing.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
🔄 IPytest extension (re)loaded.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -884,9 +1099,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "044f8550f1914cb49ee4d639a7d553c6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Do you need to call close<…" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.ContextManagers()" @@ -1040,11 +1313,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t'\n" + ] + } + ], "source": [ "import pathlib as pl\n", "\n", @@ -1067,11 +1348,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" + ] + } + ], "source": [ "import pathlib as pl\n", "\n", @@ -1096,7 +1385,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": { "tags": [] }, @@ -1125,11 +1414,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023]\n" + ] + } + ], "source": [ "import pathlib as pl\n", "import struct\n", @@ -1156,11 +1453,41 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bytes: ['0x0', '0x0', '0x0', '0x0'], integer: 0, string: \u0000\n", + "bytes: ['0x1', '0x0', '0x0', '0x0'], integer: 1, string: \u0001\n", + "bytes: ['0x2', '0x0', '0x0', '0x0'], integer: 2, string: \u0002\n", + "bytes: ['0x3', '0x0', '0x0', '0x0'], integer: 3, string: \u0003\n", + "bytes: ['0x4', '0x0', '0x0', '0x0'], integer: 4, string: \u0004\n", + "bytes: ['0x5', '0x0', '0x0', '0x0'], integer: 5, string: \u0005\n", + "bytes: ['0x3c', '0x0', '0x0', '0x0'], integer: 60, string: <\n", + "bytes: ['0x3d', '0x0', '0x0', '0x0'], integer: 61, string: =\n", + "bytes: ['0x3e', '0x0', '0x0', '0x0'], integer: 62, string: >\n", + "bytes: ['0x3f', '0x0', '0x0', '0x0'], integer: 63, string: ?\n", + "bytes: ['0x40', '0x0', '0x0', '0x0'], integer: 64, string: @\n", + "bytes: ['0x41', '0x0', '0x0', '0x0'], integer: 65, string: A\n", + "bytes: ['0x42', '0x0', '0x0', '0x0'], integer: 66, string: B\n", + "bytes: ['0x43', '0x0', '0x0', '0x0'], integer: 67, string: C\n", + "bytes: ['0x44', '0x0', '0x0', '0x0'], integer: 68, string: D\n", + "bytes: ['0x45', '0x0', '0x0', '0x0'], integer: 69, string: E\n", + "bytes: ['0x46', '0x0', '0x0', '0x0'], integer: 70, string: F\n", + "bytes: ['0x47', '0x0', '0x0', '0x0'], integer: 71, string: G\n", + "bytes: ['0x48', '0x0', '0x0', '0x0'], integer: 72, string: H\n", + "bytes: ['0x49', '0x0', '0x0', '0x0'], integer: 73, string: I\n", + "bytes: ['0x0', '0x2', '0x0', '0x0'], integer: 512, string: Ȁ\n", + "bytes: ['0x1', '0x2', '0x0', '0x0'], integer: 513, string: ȁ\n", + "bytes: ['0x2', '0x2', '0x0', '0x0'], integer: 514, string: Ȃ\n" + ] + } + ], "source": [ "for i in range(0, 6):\n", " bt = i.to_bytes(4, 'little')\n", @@ -1193,15 +1520,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The `uft8` codepoint is = 67, the bytes representation = b'C\\x00\\x00\\x00', the representation is C\n", + "The `uft8` codepoint is = 105, the bytes representation = b'i\\x00\\x00\\x00', the representation is i\n", + "The `uft8` codepoint is = 97, the bytes representation = b'a\\x00\\x00\\x00', the representation is a\n", + "The `uft8` codepoint is = 111, the bytes representation = b'o\\x00\\x00\\x00', the representation is o\n" + ] + } + ], "source": [ "message = \"Ciao\"\n", "message_secret = bytes(message, \"utf-8\")\n", - "[print(f\"The `uft8` codepoint is = {enc}, the bytes representation = {enc.to_bytes(4, 'little')}, the representation is {chr(enc)}\") for plain, enc in zip(message, message_secret)]" + "print_messages = [f\"The `uft8` codepoint is = {enc}, the bytes representation = {enc.to_bytes(4, 'little')}, the representation is {chr(enc)}\" for plain, enc in zip(message, message_secret)]\n", + "for msg in print_messages:\n", + " print(msg)" ] }, { @@ -1230,11 +1570,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['1', '2', 'some']\n", + "['3', '4', 'numbers']\n", + "['5', '6', 'here']\n" + ] + } + ], "source": [ "import csv\n", "import pathlib as pl\n", @@ -1330,7 +1680,7 @@ "tags": [] }, "source": [ - "### Exercise 1: CSV to dictionary 🌶️" + "### Exercise 1: CSV to dictionary (easy)" ] }, { @@ -1404,7 +1754,7 @@ " Returns:\n", " - A dictionary with each row represented as a key, value pair\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1414,7 +1764,7 @@ "tags": [] }, "source": [ - "### Exercise 2: Counting words 🌶️" + "### Exercise 2: Counting words (easy)" ] }, { @@ -1472,7 +1822,7 @@ " Returns:\n", " - The number of words in the file\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1482,7 +1832,7 @@ "tags": [] }, "source": [ - "### Exercise 3: Letter statistics 🌶️🌶️" + "### Exercise 3: Letter statistics (medium)" ] }, { @@ -1538,7 +1888,7 @@ " Returns:\n", " - A dictionary with a count of each letter\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1548,7 +1898,7 @@ "tags": [] }, "source": [ - "### Exercise 4: Translating words 🌶️🌶️" + "### Exercise 4: Translating words (medium)" ] }, { @@ -1615,8 +1965,7 @@ " Returns:\n", " - A list of tuples with the english / italian words\n", " \"\"\"\n", - "\n", - " pass" + " return" ] }, { @@ -1626,7 +1975,7 @@ "tags": [] }, "source": [ - "### Exercise 5: Binary format 🌶️🌶️🌶️" + "### Exercise 5: Binary format (hard)" ] }, { @@ -1678,14 +2027,14 @@ " Returns:\n", " - The secret message\n", " \"\"\"\n", - " pass" + " return" ] } ], "metadata": { "celltoolbar": "Slideshow", "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1699,7 +2048,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.4" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/05_object_oriented_programming.ipynb b/05_object_oriented_programming.ipynb index afb772b5..17b8e713 100644 --- a/05_object_oriented_programming.ipynb +++ b/05_object_oriented_programming.ipynb @@ -1700,7 +1700,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/tutorial/quiz/input_output.py b/tutorial/quiz/input_output.py index 923b907e..9bd6a539 100644 --- a/tutorial/quiz/input_output.py +++ b/tutorial/quiz/input_output.py @@ -43,7 +43,7 @@ def __init__(self, title=""): question="What happens if you call input() in the middle of a function?", options={ "The function execution stops and it waits for the user to type an input": "Correct! Input is a blocking function which waits for user to enter a string in the console and press enter.", - "The function continues its execution": "Wrong!", + "The function continues its execution": "Wrong! Input is a blocking function which waits for user input.", }, correct_answer="The function execution stops and it waits for the user to type an input", shuffle=True, @@ -57,7 +57,7 @@ def __init__(self, title=""): q1 = Question( question="What does the operator / do when applied to two Pathlib.Path objects?", options={ - "It removes the second path from the first": "Wrong, try it in the shell.", + "It removes the second path from the first": "Wrong, try it in a cell below.", "It concatenates paths": "Correct, it lets you construct a path from different segments", }, correct_answer="It concatenates paths", @@ -68,7 +68,7 @@ def __init__(self, title=""): question="If you use Pathlib, do you need to use different path separators on Windows and Linux to combine path segments?", options={ "No, you can combine Pathlib.Path objects with /": "Correct! Pathlib will then generate the correct path for your OS.", - "Yes": "Wrong! You can always use /", + "Yes": "Wrong! You can always use /", }, correct_answer="No, you can combine Pathlib.Path objects with /", shuffle=True, @@ -78,7 +78,7 @@ def __init__(self, title=""): question="""The path Pathlib.Path("./") represent a relative path. What location does it refer to?""", options={ "Relative to the current working directory, the location of the current Python script being run": "Correct!", - "Relative to the user's home directory": "Wrong!", + "Relative to the user's home directory": "Wrong! It is relative to the current working directory.", }, correct_answer="Relative to the current working directory, the location of the current Python script being run", shuffle=True, diff --git a/tutorial/tests/data/example1.csv b/tutorial/tests/data/example1.csv new file mode 100644 index 00000000..53f9e8ba --- /dev/null +++ b/tutorial/tests/data/example1.csv @@ -0,0 +1,11 @@ +this,is,data +0,1,2 +1,2,3 +2,3,4 +3,4,5 +4,5,6 +5,6,7 +6,7,8 +7,8,9 +8,9,10 +9,10,11 diff --git a/tutorial/tests/data/me.txt b/tutorial/tests/data/me.txt new file mode 100644 index 00000000..2d5fcfd7 --- /dev/null +++ b/tutorial/tests/data/me.txt @@ -0,0 +1 @@ +Simone \ No newline at end of file diff --git a/tutorial/tests/data/numbers.txt b/tutorial/tests/data/numbers.txt new file mode 100644 index 00000000..8b1acc12 --- /dev/null +++ b/tutorial/tests/data/numbers.txt @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/tutorial/tests/data/output.txt b/tutorial/tests/data/output.txt new file mode 100644 index 0000000000000000000000000000000000000000..df93f5f3f72487244976c34e85525cf445016566 GIT binary patch literal 10 RcmZQzWMXDvWn<^y1ONc904@Lk literal 0 HcmV?d00001 diff --git a/tutorial/tests/data/output1.txt b/tutorial/tests/data/output1.txt new file mode 100644 index 0000000000000000000000000000000000000000..bf7fd73a844290ab930a8665635b465aec2c9e9f GIT binary patch literal 2048 zcmV~$17HvU00h8Cwr$(is%6`@ZM&9j+qP}nwr$-3AOs^Af+GY%A{0U+48kHD!XpAA zA`&7a3Zfz!q9X=kA{JsJ4&ovn;v)ePA`ucJ36dfik|PCDA{A024bmbV(jx;hA`>zr z3$h{`vLgp_A{TNa5Aq@(@}mF(P!NSs7=b8)q9}&qD1nkFh0-X4vM7i0sDO&7gvzLb zs;GwQsDYZOh1#ftx~PZxXn=-jgvMxsrf7!dXn~e!h1O_;wrGd;=zxysgwE)KuIPsD z=z*T-h2H3czUYVk7=VEoguxhsp%{kY7=a*+#3+o$7>va@jK>5_#3W3{6imf5Oven& z#4OCl9L&W$%*O&O#3C%l5-i0sEXNA0#44=D8mz@Stj7jy#3pRU7Hq{fY{w4l#4hZ{ z9_+V|*rHLMCEjCSg)0V{)co zN~U6JreRv9V|r#_MrLAWW?@!lV|M0XPUd26=3!puV}2H301L7Z3p0>KSd_(BoF!P2 zrC6F}SeE5jo)uV;l~|coSe4aSoi$jKwOE^VSeNx!pAFcMjo6q?*p$uKoGsXrt=O7v z*p}_so*meco!FUO*p=Pbojur-z1W+5*q8m-p946MgE*K&IF!RUoFf>-ksQU*9K*33 z$MKxNiJZjAoWiM`#_62FnViMhoWr@C$N5~qgEJ1imQZ5s+3BrjLNE<%BzAZs*)Z^eo zs*xJ2iJGdJnyZCcs+C%+joPZ6+N*;)s*^gai@K_tx~qqJs+W4JkNT>g`fGp&YLEtN zh=yvIhHHd^G*Y89T4OX;<1}6qG*Oc@SyMDs(==T(G*h!QTXQs5^E6)zv`~w*SWC22 z%d}i8v{I|IT5Ggc>$F}Qv{9S1SzEMK+q7Lfv{SpZTYI!u`?OyNbWn$MSVweJ$8=mL zbW*2uT4!`t=X72dbWxXdSyyyb*K}PsbW^u89nbNdzzLnmiJinroy^Id!YQ4~sh!4YozCfCCwq#gdYY$uhG%+~ zXM2w4dYdYiX None: @pytest.mark.parametrize( "length,width", [ - (2.0, 3.0), + (2.123, 3.456), (5.0, 4.0), - (1.5, 2.5), - (0.1, 0.1), + (1.5, 2.57), + (0.109, 0.103), ], ) def test_calculate_basic_area(length: float, width: float, function_to_test): @@ -234,6 +234,28 @@ def test_calculate_area(length, width, unit, function_to_test): assert expected == result, "Incorrect area calculation or formatting" +@pytest.mark.parametrize( + "length,width,unit", + [ + (2.0, 3.0, "km"), # Invalid unit + (2.0, 3.0, ""), # Empty unit + (2.0, 3.0, "CM"), # Case sensitive check + (2.0, 3.0, "inches"), # Another invalid unit + ], +) +def test_calculate_area_invalid_units(length, width, unit, function_to_test): + """Test that the function properly handles invalid units.""" + validate_area_signature(function_to_test) + result = function_to_test(length, width, unit) + expected = f"Invalid unit: {unit}" + assert isinstance(result, str), ( + "Result should be a string like 'Invalid unit: '" + ) + assert result == expected, ( + f"Expected '{expected}' for invalid unit '{unit}', got '{result}'" + ) + + # # Exercise 3: summing anything # From 5f18cdab4ffe2f3e1c4834c9ea592a084efc0e66 Mon Sep 17 00:00:00 2001 From: Lopes Date: Tue, 4 Nov 2025 13:14:16 +0100 Subject: [PATCH 02/11] Corrected issues and tested functions in Issue #272 --- 04_input_output.ipynb | 491 ++++--------------------- tutorial/tests/test_04_input_output.py | 4 +- 2 files changed, 72 insertions(+), 423 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 4ce37f05..82ac0bb1 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -488,25 +488,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b5d9190bff57413c8c14c640aa74557a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Paths(children=(Question(children=(HTML(value='Q1: What does the operator / do w…" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.Paths()" @@ -522,34 +506,9 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
🚫 OpenAI API key is not provided.
API key is missing.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
🔄 IPytest extension (re)loaded.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -560,7 +519,7 @@ "metadata": {}, "source": [ "\n", - "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data/) (./tutorial/tests/data/) directory and return them as a list of `Path` objects\n", + "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data) directory and return them as a list of `Path` objects\n", "\n", "
\n", " Hint: The path to the data directory is available as the argument current_path of the function solution_find_all_files\n", @@ -569,24 +528,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5a82cdcf4ce1468d8718502eafc1c89a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Can you read from a file before callin…" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.ReadFiles()" @@ -873,34 +731,9 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
🚫 OpenAI API key is not provided.
API key is missing.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
🔄 IPytest extension (re)loaded.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -915,24 +748,9 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "658982e189394032bd022f74294fd393", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: What does w in the secon…" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.WriteFiles()" @@ -1057,34 +859,9 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
🚫 OpenAI API key is not provided.
API key is missing.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
🔄 IPytest extension (re)loaded.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -1099,24 +876,9 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "044f8550f1914cb49ee4d639a7d553c6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Do you need to call close<…" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.ContextManagers()" @@ -1313,19 +1036,11 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t'\n" - ] - } - ], + "outputs": [], "source": [ "import pathlib as pl\n", "\n", @@ -1348,19 +1063,11 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" - ] - } - ], + "outputs": [], "source": [ "import pathlib as pl\n", "\n", @@ -1385,7 +1092,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": { "tags": [] }, @@ -1414,19 +1121,11 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023]\n" - ] - } - ], + "outputs": [], "source": [ "import pathlib as pl\n", "import struct\n", @@ -1453,41 +1152,11 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "bytes: ['0x0', '0x0', '0x0', '0x0'], integer: 0, string: \u0000\n", - "bytes: ['0x1', '0x0', '0x0', '0x0'], integer: 1, string: \u0001\n", - "bytes: ['0x2', '0x0', '0x0', '0x0'], integer: 2, string: \u0002\n", - "bytes: ['0x3', '0x0', '0x0', '0x0'], integer: 3, string: \u0003\n", - "bytes: ['0x4', '0x0', '0x0', '0x0'], integer: 4, string: \u0004\n", - "bytes: ['0x5', '0x0', '0x0', '0x0'], integer: 5, string: \u0005\n", - "bytes: ['0x3c', '0x0', '0x0', '0x0'], integer: 60, string: <\n", - "bytes: ['0x3d', '0x0', '0x0', '0x0'], integer: 61, string: =\n", - "bytes: ['0x3e', '0x0', '0x0', '0x0'], integer: 62, string: >\n", - "bytes: ['0x3f', '0x0', '0x0', '0x0'], integer: 63, string: ?\n", - "bytes: ['0x40', '0x0', '0x0', '0x0'], integer: 64, string: @\n", - "bytes: ['0x41', '0x0', '0x0', '0x0'], integer: 65, string: A\n", - "bytes: ['0x42', '0x0', '0x0', '0x0'], integer: 66, string: B\n", - "bytes: ['0x43', '0x0', '0x0', '0x0'], integer: 67, string: C\n", - "bytes: ['0x44', '0x0', '0x0', '0x0'], integer: 68, string: D\n", - "bytes: ['0x45', '0x0', '0x0', '0x0'], integer: 69, string: E\n", - "bytes: ['0x46', '0x0', '0x0', '0x0'], integer: 70, string: F\n", - "bytes: ['0x47', '0x0', '0x0', '0x0'], integer: 71, string: G\n", - "bytes: ['0x48', '0x0', '0x0', '0x0'], integer: 72, string: H\n", - "bytes: ['0x49', '0x0', '0x0', '0x0'], integer: 73, string: I\n", - "bytes: ['0x0', '0x2', '0x0', '0x0'], integer: 512, string: Ȁ\n", - "bytes: ['0x1', '0x2', '0x0', '0x0'], integer: 513, string: ȁ\n", - "bytes: ['0x2', '0x2', '0x0', '0x0'], integer: 514, string: Ȃ\n" - ] - } - ], + "outputs": [], "source": [ "for i in range(0, 6):\n", " bt = i.to_bytes(4, 'little')\n", @@ -1520,22 +1189,11 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The `uft8` codepoint is = 67, the bytes representation = b'C\\x00\\x00\\x00', the representation is C\n", - "The `uft8` codepoint is = 105, the bytes representation = b'i\\x00\\x00\\x00', the representation is i\n", - "The `uft8` codepoint is = 97, the bytes representation = b'a\\x00\\x00\\x00', the representation is a\n", - "The `uft8` codepoint is = 111, the bytes representation = b'o\\x00\\x00\\x00', the representation is o\n" - ] - } - ], + "outputs": [], "source": [ "message = \"Ciao\"\n", "message_secret = bytes(message, \"utf-8\")\n", @@ -1565,26 +1223,16 @@ "These packages are outside of the scope of this tutorial and will not be covered here.\n", "\n", "\n", - "Let's see how to read csv files using `csv` with an example by reading [example.csv](./data/example.csv):" + "Let's see how to read csv files using `csv` with an example by reading [example.csv](./tutorial/tests/data/example.csv):" ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['1', '2', 'some']\n", - "['3', '4', 'numbers']\n", - "['5', '6', 'here']\n" - ] - } - ], + "outputs": [], "source": [ "import csv\n", "import pathlib as pl\n", @@ -1872,7 +1520,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%ipytest input_output\n", + "%%ipytest\n", "\n", "import pathlib as pl\n", "import string\n", @@ -1908,7 +1556,7 @@ "tags": [] }, "source": [ - "Write a function which takes the words from the file `english.txt` and translates them to Italian using the dictionary file `dict.csv`. The output should be a **list of tuples** with the pair `italian, english` if the word is found and nothing otherwise.\n", + "Write a function which takes the words from the file `english.txt` and translates them to Italian using the dictionary file `dict.csv`. The output should be a **list of tuples** with the pair `italian, english` **if the word is found and nothing otherwise**.\n", "\n", "For example, given the `english.txt` file:\n", "\n", @@ -1950,6 +1598,7 @@ "source": [ "%%ipytest\n", "\n", + "import csv\n", "import pathlib as pl\n", "\n", "def solution_exercise4(english: pl.Path, dictionary: pl.Path) -> list[(str, str)]:\n", diff --git a/tutorial/tests/test_04_input_output.py b/tutorial/tests/test_04_input_output.py index b7b9fa69..13fb902c 100644 --- a/tutorial/tests/test_04_input_output.py +++ b/tutorial/tests/test_04_input_output.py @@ -254,10 +254,10 @@ def reference_exercise5(secret_file: pl.Path) -> str: def test_exercise5(function_to_test): message = get_data("secret_message.dat") # Save the original content of the input file - original_content = message.read_text() + original_content = message.read_bytes() try: assert function_to_test(message) == reference_exercise5(message) finally: # Reset the input file to its original content - message.write_text(original_content) + message.write_bytes(original_content) From 3e8c8d9b8d173599d2f3bee1baf73c5fed1748fd Mon Sep 17 00:00:00 2001 From: Lopes Date: Tue, 4 Nov 2025 13:15:52 +0100 Subject: [PATCH 03/11] Corrected directories hyperlinks --- 04_input_output.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 82ac0bb1..6617a382 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -519,7 +519,7 @@ "metadata": {}, "source": [ "\n", - "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data) directory and return them as a list of `Path` objects\n", + "1. Modify the function `solution_find_all_files` to find all files and directories in the ```/tutorial/tests/data``` directory and return them as a list of `Path` objects\n", "\n", "
\n", " Hint: The path to the data directory is available as the argument current_path of the function solution_find_all_files\n", @@ -779,7 +779,7 @@ "- We use `write` to write a *string* to the file. Other types of object should be converted to string before being written.\n", "\n", "\n", - "Let's see this in action by writing your name in a file called `me.txt` in [./tutorial/tests/data](./tutorial/tests/data/)" + "Let's see this in action by writing your name in a file called `me.txt` in ```/tutorial/tests/data```" ] }, { From c13d7e2de53810d19df6d64b866c49163aeb1654 Mon Sep 17 00:00:00 2001 From: Lopes Date: Mon, 3 Nov 2025 18:08:26 +0100 Subject: [PATCH 04/11] Resolving part of the issue #272 --- 04_input_output.ipynb | 559 ++++++++++++++++++++++----- 05_object_oriented_programming.ipynb | 2 +- tutorial/quiz/input_output.py | 8 +- tutorial/tests/data/example1.csv | 11 + tutorial/tests/data/me.txt | 1 + tutorial/tests/data/numbers.txt | 10 + tutorial/tests/data/output.txt | Bin 0 -> 10 bytes tutorial/tests/data/output1.txt | Bin 0 -> 2048 bytes 8 files changed, 481 insertions(+), 110 deletions(-) create mode 100644 tutorial/tests/data/example1.csv create mode 100644 tutorial/tests/data/me.txt create mode 100644 tutorial/tests/data/numbers.txt create mode 100644 tutorial/tests/data/output.txt create mode 100644 tutorial/tests/data/output1.txt diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 7a4c3153..4ce37f05 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -40,11 +40,11 @@ " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", " - [Quiz on CSV](#Quiz-on-CSV)\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-🌶️🌶️🌶️)" + " - [Exercise 1: CSV to dictionary (easy)](#Exercise-1:-CSV-to-dictionary-(easy))\n", + " - [Exercise 2: Counting words (easy)](#Exercise-2:-Counting-words-(easy))\n", + " - [Exercise 3: Letter statistics (medium)](#Exercise-3:-Letter-statistics-(medium))\n", + " - [Exercise 4: Translating words (medium)](#Exercise-4:-Translating-words-(medium))\n", + " - [Exercise 5: Binary format (hard)](#Exercise-5:-Binary-format-(hard))" ] }, { @@ -75,13 +75,7 @@ "- connecting to databases or other network services\n", "\n", "\n", - "The majority of these operations are covered by the Python standard library. We are going to see how to use them in this chapter.\n", - "\n", - "
\n", - "

Note

\n", - "In reference to the chapter on functional programming, it is interesting to note that these functions perform side-effects. Therefore, any code containing these operations is no longer pure and is not referentially transparent. The same function can return different values for the same argument if called multiple times, and the function can have long-distance effects. That means they can modify the program state elsewhere, leading to unexpected results.

\n", - "Therefore, we suggest separating input and output from the other computations in your program. For example, if you have a complex calculation requiring several user inputs at several stages of the process, consider writing a function that only performs the calculation given all inputs and then requires all inputs separately, for example, through a single file. This makes your code easier to debug, test and understand.\n", - "
" + "The majority of these operations are covered by the Python standard library. We are going to see how to use them in this chapter." ] }, { @@ -108,8 +102,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "It is also possible to print any other python object using `print`. \n", - "In that case, the `__str__` **magic method** on that object's class is [called](https://docs.python.org/3/reference/datamodel.html#object.__str__)." + "It is also possible to print any other python object using `print`." ] }, { @@ -154,7 +147,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", @@ -245,7 +240,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%ipytest debug\n", + "%%ipytest\n", "\n", "def solution_print_odd(n: int) -> None: \n", " \"\"\"Prints all odd numbers from 1 to n\n", @@ -255,8 +250,7 @@ "\n", " Returns:\n", " - None (prints to console)\n", - " \"\"\"\n", - " pass" + " \"\"\"" ] }, { @@ -287,7 +281,6 @@ " Returns:\n", " - None (prints to console)\n", " \"\"\"\n", - " pass\n", "\n", "solution_print_salutation()" ] @@ -495,9 +488,25 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b5d9190bff57413c8c14c640aa74557a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Paths(children=(Question(children=(HTML(value='Q1: What does the operator / do w…" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.Paths()" @@ -513,9 +522,34 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
🚫 OpenAI API key is not provided.
API key is missing.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
🔄 IPytest extension (re)loaded.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -535,9 +569,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5a82cdcf4ce1468d8718502eafc1c89a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Can you read from a file before callin…" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.ReadFiles()" @@ -739,9 +873,34 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
🚫 OpenAI API key is not provided.
API key is missing.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
🔄 IPytest extension (re)loaded.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -756,9 +915,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "658982e189394032bd022f74294fd393", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: What does w in the secon…" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.WriteFiles()" @@ -867,9 +1057,34 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
🚫 OpenAI API key is not provided.
API key is missing.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
🔄 IPytest extension (re)loaded.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -884,9 +1099,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "044f8550f1914cb49ee4d639a7d553c6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Do you need to call close<…" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tutorial.quiz.input_output as op\n", "op.ContextManagers()" @@ -1040,11 +1313,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t'\n" + ] + } + ], "source": [ "import pathlib as pl\n", "\n", @@ -1067,11 +1348,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" + ] + } + ], "source": [ "import pathlib as pl\n", "\n", @@ -1096,7 +1385,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": { "tags": [] }, @@ -1125,11 +1414,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023]\n" + ] + } + ], "source": [ "import pathlib as pl\n", "import struct\n", @@ -1156,11 +1453,41 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bytes: ['0x0', '0x0', '0x0', '0x0'], integer: 0, string: \u0000\n", + "bytes: ['0x1', '0x0', '0x0', '0x0'], integer: 1, string: \u0001\n", + "bytes: ['0x2', '0x0', '0x0', '0x0'], integer: 2, string: \u0002\n", + "bytes: ['0x3', '0x0', '0x0', '0x0'], integer: 3, string: \u0003\n", + "bytes: ['0x4', '0x0', '0x0', '0x0'], integer: 4, string: \u0004\n", + "bytes: ['0x5', '0x0', '0x0', '0x0'], integer: 5, string: \u0005\n", + "bytes: ['0x3c', '0x0', '0x0', '0x0'], integer: 60, string: <\n", + "bytes: ['0x3d', '0x0', '0x0', '0x0'], integer: 61, string: =\n", + "bytes: ['0x3e', '0x0', '0x0', '0x0'], integer: 62, string: >\n", + "bytes: ['0x3f', '0x0', '0x0', '0x0'], integer: 63, string: ?\n", + "bytes: ['0x40', '0x0', '0x0', '0x0'], integer: 64, string: @\n", + "bytes: ['0x41', '0x0', '0x0', '0x0'], integer: 65, string: A\n", + "bytes: ['0x42', '0x0', '0x0', '0x0'], integer: 66, string: B\n", + "bytes: ['0x43', '0x0', '0x0', '0x0'], integer: 67, string: C\n", + "bytes: ['0x44', '0x0', '0x0', '0x0'], integer: 68, string: D\n", + "bytes: ['0x45', '0x0', '0x0', '0x0'], integer: 69, string: E\n", + "bytes: ['0x46', '0x0', '0x0', '0x0'], integer: 70, string: F\n", + "bytes: ['0x47', '0x0', '0x0', '0x0'], integer: 71, string: G\n", + "bytes: ['0x48', '0x0', '0x0', '0x0'], integer: 72, string: H\n", + "bytes: ['0x49', '0x0', '0x0', '0x0'], integer: 73, string: I\n", + "bytes: ['0x0', '0x2', '0x0', '0x0'], integer: 512, string: Ȁ\n", + "bytes: ['0x1', '0x2', '0x0', '0x0'], integer: 513, string: ȁ\n", + "bytes: ['0x2', '0x2', '0x0', '0x0'], integer: 514, string: Ȃ\n" + ] + } + ], "source": [ "for i in range(0, 6):\n", " bt = i.to_bytes(4, 'little')\n", @@ -1193,15 +1520,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The `uft8` codepoint is = 67, the bytes representation = b'C\\x00\\x00\\x00', the representation is C\n", + "The `uft8` codepoint is = 105, the bytes representation = b'i\\x00\\x00\\x00', the representation is i\n", + "The `uft8` codepoint is = 97, the bytes representation = b'a\\x00\\x00\\x00', the representation is a\n", + "The `uft8` codepoint is = 111, the bytes representation = b'o\\x00\\x00\\x00', the representation is o\n" + ] + } + ], "source": [ "message = \"Ciao\"\n", "message_secret = bytes(message, \"utf-8\")\n", - "[print(f\"The `uft8` codepoint is = {enc}, the bytes representation = {enc.to_bytes(4, 'little')}, the representation is {chr(enc)}\") for plain, enc in zip(message, message_secret)]" + "print_messages = [f\"The `uft8` codepoint is = {enc}, the bytes representation = {enc.to_bytes(4, 'little')}, the representation is {chr(enc)}\" for plain, enc in zip(message, message_secret)]\n", + "for msg in print_messages:\n", + " print(msg)" ] }, { @@ -1230,11 +1570,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['1', '2', 'some']\n", + "['3', '4', 'numbers']\n", + "['5', '6', 'here']\n" + ] + } + ], "source": [ "import csv\n", "import pathlib as pl\n", @@ -1330,7 +1680,7 @@ "tags": [] }, "source": [ - "### Exercise 1: CSV to dictionary 🌶️" + "### Exercise 1: CSV to dictionary (easy)" ] }, { @@ -1404,7 +1754,7 @@ " Returns:\n", " - A dictionary with each row represented as a key, value pair\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1414,7 +1764,7 @@ "tags": [] }, "source": [ - "### Exercise 2: Counting words 🌶️" + "### Exercise 2: Counting words (easy)" ] }, { @@ -1472,7 +1822,7 @@ " Returns:\n", " - The number of words in the file\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1482,7 +1832,7 @@ "tags": [] }, "source": [ - "### Exercise 3: Letter statistics 🌶️🌶️" + "### Exercise 3: Letter statistics (medium)" ] }, { @@ -1538,7 +1888,7 @@ " Returns:\n", " - A dictionary with a count of each letter\n", " \"\"\"\n", - " pass" + " return" ] }, { @@ -1548,7 +1898,7 @@ "tags": [] }, "source": [ - "### Exercise 4: Translating words 🌶️🌶️" + "### Exercise 4: Translating words (medium)" ] }, { @@ -1615,8 +1965,7 @@ " Returns:\n", " - A list of tuples with the english / italian words\n", " \"\"\"\n", - "\n", - " pass" + " return" ] }, { @@ -1626,7 +1975,7 @@ "tags": [] }, "source": [ - "### Exercise 5: Binary format 🌶️🌶️🌶️" + "### Exercise 5: Binary format (hard)" ] }, { @@ -1678,14 +2027,14 @@ " Returns:\n", " - The secret message\n", " \"\"\"\n", - " pass" + " return" ] } ], "metadata": { "celltoolbar": "Slideshow", "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1699,7 +2048,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.4" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/05_object_oriented_programming.ipynb b/05_object_oriented_programming.ipynb index afb772b5..17b8e713 100644 --- a/05_object_oriented_programming.ipynb +++ b/05_object_oriented_programming.ipynb @@ -1700,7 +1700,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/tutorial/quiz/input_output.py b/tutorial/quiz/input_output.py index 923b907e..9bd6a539 100644 --- a/tutorial/quiz/input_output.py +++ b/tutorial/quiz/input_output.py @@ -43,7 +43,7 @@ def __init__(self, title=""): question="What happens if you call input() in the middle of a function?", options={ "The function execution stops and it waits for the user to type an input": "Correct! Input is a blocking function which waits for user to enter a string in the console and press enter.", - "The function continues its execution": "Wrong!", + "The function continues its execution": "Wrong! Input is a blocking function which waits for user input.", }, correct_answer="The function execution stops and it waits for the user to type an input", shuffle=True, @@ -57,7 +57,7 @@ def __init__(self, title=""): q1 = Question( question="What does the operator / do when applied to two Pathlib.Path objects?", options={ - "It removes the second path from the first": "Wrong, try it in the shell.", + "It removes the second path from the first": "Wrong, try it in a cell below.", "It concatenates paths": "Correct, it lets you construct a path from different segments", }, correct_answer="It concatenates paths", @@ -68,7 +68,7 @@ def __init__(self, title=""): question="If you use Pathlib, do you need to use different path separators on Windows and Linux to combine path segments?", options={ "No, you can combine Pathlib.Path objects with /": "Correct! Pathlib will then generate the correct path for your OS.", - "Yes": "Wrong! You can always use /", + "Yes": "Wrong! You can always use /", }, correct_answer="No, you can combine Pathlib.Path objects with /", shuffle=True, @@ -78,7 +78,7 @@ def __init__(self, title=""): question="""The path Pathlib.Path("./") represent a relative path. What location does it refer to?""", options={ "Relative to the current working directory, the location of the current Python script being run": "Correct!", - "Relative to the user's home directory": "Wrong!", + "Relative to the user's home directory": "Wrong! It is relative to the current working directory.", }, correct_answer="Relative to the current working directory, the location of the current Python script being run", shuffle=True, diff --git a/tutorial/tests/data/example1.csv b/tutorial/tests/data/example1.csv new file mode 100644 index 00000000..53f9e8ba --- /dev/null +++ b/tutorial/tests/data/example1.csv @@ -0,0 +1,11 @@ +this,is,data +0,1,2 +1,2,3 +2,3,4 +3,4,5 +4,5,6 +5,6,7 +6,7,8 +7,8,9 +8,9,10 +9,10,11 diff --git a/tutorial/tests/data/me.txt b/tutorial/tests/data/me.txt new file mode 100644 index 00000000..2d5fcfd7 --- /dev/null +++ b/tutorial/tests/data/me.txt @@ -0,0 +1 @@ +Simone \ No newline at end of file diff --git a/tutorial/tests/data/numbers.txt b/tutorial/tests/data/numbers.txt new file mode 100644 index 00000000..8b1acc12 --- /dev/null +++ b/tutorial/tests/data/numbers.txt @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/tutorial/tests/data/output.txt b/tutorial/tests/data/output.txt new file mode 100644 index 0000000000000000000000000000000000000000..df93f5f3f72487244976c34e85525cf445016566 GIT binary patch literal 10 RcmZQzWMXDvWn<^y1ONc904@Lk literal 0 HcmV?d00001 diff --git a/tutorial/tests/data/output1.txt b/tutorial/tests/data/output1.txt new file mode 100644 index 0000000000000000000000000000000000000000..bf7fd73a844290ab930a8665635b465aec2c9e9f GIT binary patch literal 2048 zcmV~$17HvU00h8Cwr$(is%6`@ZM&9j+qP}nwr$-3AOs^Af+GY%A{0U+48kHD!XpAA zA`&7a3Zfz!q9X=kA{JsJ4&ovn;v)ePA`ucJ36dfik|PCDA{A024bmbV(jx;hA`>zr z3$h{`vLgp_A{TNa5Aq@(@}mF(P!NSs7=b8)q9}&qD1nkFh0-X4vM7i0sDO&7gvzLb zs;GwQsDYZOh1#ftx~PZxXn=-jgvMxsrf7!dXn~e!h1O_;wrGd;=zxysgwE)KuIPsD z=z*T-h2H3czUYVk7=VEoguxhsp%{kY7=a*+#3+o$7>va@jK>5_#3W3{6imf5Oven& z#4OCl9L&W$%*O&O#3C%l5-i0sEXNA0#44=D8mz@Stj7jy#3pRU7Hq{fY{w4l#4hZ{ z9_+V|*rHLMCEjCSg)0V{)co zN~U6JreRv9V|r#_MrLAWW?@!lV|M0XPUd26=3!puV}2H301L7Z3p0>KSd_(BoF!P2 zrC6F}SeE5jo)uV;l~|coSe4aSoi$jKwOE^VSeNx!pAFcMjo6q?*p$uKoGsXrt=O7v z*p}_so*meco!FUO*p=Pbojur-z1W+5*q8m-p946MgE*K&IF!RUoFf>-ksQU*9K*33 z$MKxNiJZjAoWiM`#_62FnViMhoWr@C$N5~qgEJ1imQZ5s+3BrjLNE<%BzAZs*)Z^eo zs*xJ2iJGdJnyZCcs+C%+joPZ6+N*;)s*^gai@K_tx~qqJs+W4JkNT>g`fGp&YLEtN zh=yvIhHHd^G*Y89T4OX;<1}6qG*Oc@SyMDs(==T(G*h!QTXQs5^E6)zv`~w*SWC22 z%d}i8v{I|IT5Ggc>$F}Qv{9S1SzEMK+q7Lfv{SpZTYI!u`?OyNbWn$MSVweJ$8=mL zbW*2uT4!`t=X72dbWxXdSyyyb*K}PsbW^u89nbNdzzLnmiJinroy^Id!YQ4~sh!4YozCfCCwq#gdYY$uhG%+~ zXM2w4dYdYiX Date: Tue, 4 Nov 2025 13:14:16 +0100 Subject: [PATCH 05/11] Corrected issues and tested functions in Issue #272 --- 04_input_output.ipynb | 491 ++++--------------------- tutorial/tests/test_04_input_output.py | 4 +- 2 files changed, 72 insertions(+), 423 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 4ce37f05..82ac0bb1 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -488,25 +488,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b5d9190bff57413c8c14c640aa74557a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Paths(children=(Question(children=(HTML(value='Q1: What does the operator / do w…" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.Paths()" @@ -522,34 +506,9 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
🚫 OpenAI API key is not provided.
API key is missing.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
🔄 IPytest extension (re)loaded.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -560,7 +519,7 @@ "metadata": {}, "source": [ "\n", - "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data/) (./tutorial/tests/data/) directory and return them as a list of `Path` objects\n", + "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data) directory and return them as a list of `Path` objects\n", "\n", "
\n", " Hint: The path to the data directory is available as the argument current_path of the function solution_find_all_files\n", @@ -569,24 +528,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5a82cdcf4ce1468d8718502eafc1c89a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Can you read from a file before callin…" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.ReadFiles()" @@ -873,34 +731,9 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
🚫 OpenAI API key is not provided.
API key is missing.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
🔄 IPytest extension (re)loaded.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -915,24 +748,9 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "658982e189394032bd022f74294fd393", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: What does w in the secon…" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.WriteFiles()" @@ -1057,34 +859,9 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
🚫 OpenAI API key is not provided.
API key is missing.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
🔄 IPytest extension (re)loaded.
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%reload_ext tutorial.tests.testsuite" ] @@ -1099,24 +876,9 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "044f8550f1914cb49ee4d639a7d553c6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': 'HTML(value=\\'
Q1: Do you need to call close<…" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", "op.ContextManagers()" @@ -1313,19 +1036,11 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t'\n" - ] - } - ], + "outputs": [], "source": [ "import pathlib as pl\n", "\n", @@ -1348,19 +1063,11 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" - ] - } - ], + "outputs": [], "source": [ "import pathlib as pl\n", "\n", @@ -1385,7 +1092,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": { "tags": [] }, @@ -1414,19 +1121,11 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023]\n" - ] - } - ], + "outputs": [], "source": [ "import pathlib as pl\n", "import struct\n", @@ -1453,41 +1152,11 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "bytes: ['0x0', '0x0', '0x0', '0x0'], integer: 0, string: \u0000\n", - "bytes: ['0x1', '0x0', '0x0', '0x0'], integer: 1, string: \u0001\n", - "bytes: ['0x2', '0x0', '0x0', '0x0'], integer: 2, string: \u0002\n", - "bytes: ['0x3', '0x0', '0x0', '0x0'], integer: 3, string: \u0003\n", - "bytes: ['0x4', '0x0', '0x0', '0x0'], integer: 4, string: \u0004\n", - "bytes: ['0x5', '0x0', '0x0', '0x0'], integer: 5, string: \u0005\n", - "bytes: ['0x3c', '0x0', '0x0', '0x0'], integer: 60, string: <\n", - "bytes: ['0x3d', '0x0', '0x0', '0x0'], integer: 61, string: =\n", - "bytes: ['0x3e', '0x0', '0x0', '0x0'], integer: 62, string: >\n", - "bytes: ['0x3f', '0x0', '0x0', '0x0'], integer: 63, string: ?\n", - "bytes: ['0x40', '0x0', '0x0', '0x0'], integer: 64, string: @\n", - "bytes: ['0x41', '0x0', '0x0', '0x0'], integer: 65, string: A\n", - "bytes: ['0x42', '0x0', '0x0', '0x0'], integer: 66, string: B\n", - "bytes: ['0x43', '0x0', '0x0', '0x0'], integer: 67, string: C\n", - "bytes: ['0x44', '0x0', '0x0', '0x0'], integer: 68, string: D\n", - "bytes: ['0x45', '0x0', '0x0', '0x0'], integer: 69, string: E\n", - "bytes: ['0x46', '0x0', '0x0', '0x0'], integer: 70, string: F\n", - "bytes: ['0x47', '0x0', '0x0', '0x0'], integer: 71, string: G\n", - "bytes: ['0x48', '0x0', '0x0', '0x0'], integer: 72, string: H\n", - "bytes: ['0x49', '0x0', '0x0', '0x0'], integer: 73, string: I\n", - "bytes: ['0x0', '0x2', '0x0', '0x0'], integer: 512, string: Ȁ\n", - "bytes: ['0x1', '0x2', '0x0', '0x0'], integer: 513, string: ȁ\n", - "bytes: ['0x2', '0x2', '0x0', '0x0'], integer: 514, string: Ȃ\n" - ] - } - ], + "outputs": [], "source": [ "for i in range(0, 6):\n", " bt = i.to_bytes(4, 'little')\n", @@ -1520,22 +1189,11 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The `uft8` codepoint is = 67, the bytes representation = b'C\\x00\\x00\\x00', the representation is C\n", - "The `uft8` codepoint is = 105, the bytes representation = b'i\\x00\\x00\\x00', the representation is i\n", - "The `uft8` codepoint is = 97, the bytes representation = b'a\\x00\\x00\\x00', the representation is a\n", - "The `uft8` codepoint is = 111, the bytes representation = b'o\\x00\\x00\\x00', the representation is o\n" - ] - } - ], + "outputs": [], "source": [ "message = \"Ciao\"\n", "message_secret = bytes(message, \"utf-8\")\n", @@ -1565,26 +1223,16 @@ "These packages are outside of the scope of this tutorial and will not be covered here.\n", "\n", "\n", - "Let's see how to read csv files using `csv` with an example by reading [example.csv](./data/example.csv):" + "Let's see how to read csv files using `csv` with an example by reading [example.csv](./tutorial/tests/data/example.csv):" ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['1', '2', 'some']\n", - "['3', '4', 'numbers']\n", - "['5', '6', 'here']\n" - ] - } - ], + "outputs": [], "source": [ "import csv\n", "import pathlib as pl\n", @@ -1872,7 +1520,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%ipytest input_output\n", + "%%ipytest\n", "\n", "import pathlib as pl\n", "import string\n", @@ -1908,7 +1556,7 @@ "tags": [] }, "source": [ - "Write a function which takes the words from the file `english.txt` and translates them to Italian using the dictionary file `dict.csv`. The output should be a **list of tuples** with the pair `italian, english` if the word is found and nothing otherwise.\n", + "Write a function which takes the words from the file `english.txt` and translates them to Italian using the dictionary file `dict.csv`. The output should be a **list of tuples** with the pair `italian, english` **if the word is found and nothing otherwise**.\n", "\n", "For example, given the `english.txt` file:\n", "\n", @@ -1950,6 +1598,7 @@ "source": [ "%%ipytest\n", "\n", + "import csv\n", "import pathlib as pl\n", "\n", "def solution_exercise4(english: pl.Path, dictionary: pl.Path) -> list[(str, str)]:\n", diff --git a/tutorial/tests/test_04_input_output.py b/tutorial/tests/test_04_input_output.py index b7b9fa69..13fb902c 100644 --- a/tutorial/tests/test_04_input_output.py +++ b/tutorial/tests/test_04_input_output.py @@ -254,10 +254,10 @@ def reference_exercise5(secret_file: pl.Path) -> str: def test_exercise5(function_to_test): message = get_data("secret_message.dat") # Save the original content of the input file - original_content = message.read_text() + original_content = message.read_bytes() try: assert function_to_test(message) == reference_exercise5(message) finally: # Reset the input file to its original content - message.write_text(original_content) + message.write_bytes(original_content) From ee27b63b3ed704d12da02c4ec6cbe1570484df2c Mon Sep 17 00:00:00 2001 From: Lopes Date: Tue, 4 Nov 2025 13:15:52 +0100 Subject: [PATCH 06/11] Corrected directories hyperlinks --- 04_input_output.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 82ac0bb1..6617a382 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -519,7 +519,7 @@ "metadata": {}, "source": [ "\n", - "1. Modify the function `solution_find_all_files` to find all files and directories in the [data](./tutorial/tests/data) directory and return them as a list of `Path` objects\n", + "1. Modify the function `solution_find_all_files` to find all files and directories in the ```/tutorial/tests/data``` directory and return them as a list of `Path` objects\n", "\n", "
\n", " Hint: The path to the data directory is available as the argument current_path of the function solution_find_all_files\n", @@ -779,7 +779,7 @@ "- We use `write` to write a *string* to the file. Other types of object should be converted to string before being written.\n", "\n", "\n", - "Let's see this in action by writing your name in a file called `me.txt` in [./tutorial/tests/data](./tutorial/tests/data/)" + "Let's see this in action by writing your name in a file called `me.txt` in ```/tutorial/tests/data```" ] }, { From 5bd589eb6742c15e877451b291ee8d04a405d4b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:31:55 +0000 Subject: [PATCH 07/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 04_input_output.ipynb | 4 +--- tutorial/tests/data/example1.csv | 22 +++++++++++----------- tutorial/tests/data/me.txt | 2 +- tutorial/tests/data/output.txt | Bin 10 -> 10 bytes tutorial/tests/data/output1.txt | Bin 2048 -> 2049 bytes 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 6617a382..6c94bd5d 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -147,9 +147,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", diff --git a/tutorial/tests/data/example1.csv b/tutorial/tests/data/example1.csv index 53f9e8ba..97cf6750 100644 --- a/tutorial/tests/data/example1.csv +++ b/tutorial/tests/data/example1.csv @@ -1,11 +1,11 @@ -this,is,data -0,1,2 -1,2,3 -2,3,4 -3,4,5 -4,5,6 -5,6,7 -6,7,8 -7,8,9 -8,9,10 -9,10,11 +this,is,data +0,1,2 +1,2,3 +2,3,4 +3,4,5 +4,5,6 +5,6,7 +6,7,8 +7,8,9 +8,9,10 +9,10,11 \ No newline at end of file diff --git a/tutorial/tests/data/me.txt b/tutorial/tests/data/me.txt index 2d5fcfd7..e09cfaf8 100644 --- a/tutorial/tests/data/me.txt +++ b/tutorial/tests/data/me.txt @@ -1 +1 @@ -Simone \ No newline at end of file +Simone diff --git a/tutorial/tests/data/output.txt b/tutorial/tests/data/output.txt index df93f5f3f72487244976c34e85525cf445016566..6a1b60378403dc4bfc05a02f2d0992d4d46b82c6 100644 GIT binary patch literal 10 RcmZQzWMXDvWn<^y0ssK8051Rl literal 10 RcmZQzWMXDvWn<^y1ONc904@Lk diff --git a/tutorial/tests/data/output1.txt b/tutorial/tests/data/output1.txt index bf7fd73a844290ab930a8665635b465aec2c9e9f..f7dff7ae65669a88e2b23c35bf3c5282b6b38f1a 100644 GIT binary patch delta 8 PcmZn=XcSn$!N>&w3UUF} delta 6 NcmZn^Xb@Py0RRTn0m=XX From 4717bf309ceaa7ed1423ac1f96da250ac5f9fe92 Mon Sep 17 00:00:00 2001 From: Lopes Date: Tue, 4 Nov 2025 13:33:02 +0100 Subject: [PATCH 08/11] Running pre-commit --- 04_input_output.ipynb | 4 +--- tutorial/tests/data/example1.csv | 22 +++++++++++----------- tutorial/tests/data/me.txt | 2 +- tutorial/tests/data/output.txt | Bin 10 -> 10 bytes tutorial/tests/data/output1.txt | Bin 2048 -> 2049 bytes 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 6617a382..6c94bd5d 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -147,9 +147,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ "import tutorial.quiz.input_output as op\n", diff --git a/tutorial/tests/data/example1.csv b/tutorial/tests/data/example1.csv index 53f9e8ba..0c6b9092 100644 --- a/tutorial/tests/data/example1.csv +++ b/tutorial/tests/data/example1.csv @@ -1,11 +1,11 @@ -this,is,data -0,1,2 -1,2,3 -2,3,4 -3,4,5 -4,5,6 -5,6,7 -6,7,8 -7,8,9 -8,9,10 -9,10,11 +this,is,data +0,1,2 +1,2,3 +2,3,4 +3,4,5 +4,5,6 +5,6,7 +6,7,8 +7,8,9 +8,9,10 +9,10,11 \ No newline at end of file diff --git a/tutorial/tests/data/me.txt b/tutorial/tests/data/me.txt index 2d5fcfd7..e09cfaf8 100644 --- a/tutorial/tests/data/me.txt +++ b/tutorial/tests/data/me.txt @@ -1 +1 @@ -Simone \ No newline at end of file +Simone diff --git a/tutorial/tests/data/output.txt b/tutorial/tests/data/output.txt index df93f5f3f72487244976c34e85525cf445016566..6a1b60378403dc4bfc05a02f2d0992d4d46b82c6 100644 GIT binary patch literal 10 RcmZQzWMXDvWn<^y0ssK8051Rl literal 10 RcmZQzWMXDvWn<^y1ONc904@Lk diff --git a/tutorial/tests/data/output1.txt b/tutorial/tests/data/output1.txt index bf7fd73a844290ab930a8665635b465aec2c9e9f..f7dff7ae65669a88e2b23c35bf3c5282b6b38f1a 100644 GIT binary patch delta 8 PcmZn=XcSn$!N>&w3UUF} delta 6 NcmZn^Xb@Py0RRTn0m=XX From 500e08347ecd451f26bbee26e9795ab943cf2516 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:37:34 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tutorial/tests/data/example1.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tests/data/example1.csv b/tutorial/tests/data/example1.csv index 3870be47..ab2340cd 100644 --- a/tutorial/tests/data/example1.csv +++ b/tutorial/tests/data/example1.csv @@ -21,4 +21,4 @@ this,is,data 7,8,9 8,9,10 >>>>>>> 5bd589eb6742c15e877451b291ee8d04a405d4b1 -9,10,11 \ No newline at end of file +9,10,11 From 6ab78a7cccbb8e715acb123eda8dc2ee6d914e10 Mon Sep 17 00:00:00 2001 From: Lopes Date: Tue, 11 Nov 2025 14:07:17 +0100 Subject: [PATCH 10/11] Correct filepath --- 04_input_output.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index 6c94bd5d..c77e582f 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -619,7 +619,7 @@ "We now want to learn how to read text from a file. \n", "Let's see how to do this with an example: we want to open the file [hello.txt](./tutorial/tests/data/hello.txt) and read its contents.\n", "\n", - "1. The path is already identified, we know the file is in `./tutorial/tests/data/hello.txt`. We save this in a variable `path`.\n", + "1. The path is already identified, we know the file is in [hello.txt](./tutorial/tests/data/hello.txt). We save this in a variable `path`.\n", "2. We now can use the built-in function [`open`](https://docs.python.org/3/library/functions.html#open) to open the file. This function returns a [file object](https://docs.python.org/3/glossary.html#term-file-object) that we can use to further manipulate the file. To ensure we only open the file for reading, we pass the string \"r\" to the second argument of `open`.\n", "3. Now we can read the contents using `read`, `readline` or `readlines`. `read` reads the whole file content into a single string, `readline` reads one line, while `readlines` reads the whole file content as a list of strings, one item per line in the file. This knowledge is useful when we only want to read part of a file or when the file is too big to fit in memory and we can only read parts.\n", "4. Finally, we close the file using the `close` method on the file object.\n" From 9c639fa33573d2c6ed953446443de468b51f5bff Mon Sep 17 00:00:00 2001 From: Despina Adamopoulou Date: Tue, 11 Nov 2025 16:41:47 +0100 Subject: [PATCH 11/11] final fixes --- 04_input_output.ipynb | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/04_input_output.ipynb b/04_input_output.ipynb index c77e582f..0666a1d6 100644 --- a/04_input_output.ipynb +++ b/04_input_output.ipynb @@ -40,11 +40,11 @@ " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", " - [Quiz on CSV](#Quiz-on-CSV)\n", " - [Exercises](#Exercises)\n", - " - [Exercise 1: CSV to dictionary (easy)](#Exercise-1:-CSV-to-dictionary-(easy))\n", - " - [Exercise 2: Counting words (easy)](#Exercise-2:-Counting-words-(easy))\n", - " - [Exercise 3: Letter statistics (medium)](#Exercise-3:-Letter-statistics-(medium))\n", - " - [Exercise 4: Translating words (medium)](#Exercise-4:-Translating-words-(medium))\n", - " - [Exercise 5: Binary format (hard)](#Exercise-5:-Binary-format-(hard))" + " - [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)" ] }, { @@ -1326,7 +1326,7 @@ "tags": [] }, "source": [ - "### Exercise 1: CSV to dictionary (easy)" + "### Exercise 1: CSV to dictionary" ] }, { @@ -1336,6 +1336,8 @@ "tags": [] }, "source": [ + "**Difficulty: 🌶️**\n", + "\n", "Write a function that reads a CSV file and returns a dictionary.\n", "\n", "- The dictionary keys are in the first **column**\n", @@ -1410,7 +1412,7 @@ "tags": [] }, "source": [ - "### Exercise 2: Counting words (easy)" + "### Exercise 2: Counting words" ] }, { @@ -1420,6 +1422,8 @@ "tags": [] }, "source": [ + "**Difficulty: 🌶️**\n", + "\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", @@ -1439,7 +1443,7 @@ " The file is available as the parameter input_file of solution_exercise2 function\n", " \n", "
  • \n", - " A word consists of printable characters without whitespaces, line breaks etc. Have a look at the basic_datatypes notebook if you forgot how to split a text into it's words.\n", + " A word consists of printable characters without whitespaces, line breaks etc. Have a look at the basic_datatypes notebook if you forgot how to split a text into its words.\n", "
  • \n", " \n", "
    \n", @@ -1478,7 +1482,7 @@ "tags": [] }, "source": [ - "### Exercise 3: Letter statistics (medium)" + "### Exercise 3: Letter statistics" ] }, { @@ -1488,7 +1492,9 @@ "tags": [] }, "source": [ - "Write a function that reads all the lines from `lines.txt` and counts the occurences of every letter present in all the words.\n", + "**Difficulty: 🌶️🌶️**\n", + "\n", + "Write a function that reads all the lines from [lines.txt](./tutorial/tests/data/lines.txt) and counts the occurences of every letter present in all the words.\n", "\n", "The result should be a dictionary, where each key-value pair is like `{letter: count}`. For example, `{a: 5}` means that the letter `a` appeared five times in this file.\n", "\n", @@ -1544,7 +1550,7 @@ "tags": [] }, "source": [ - "### Exercise 4: Translating words (medium)" + "### Exercise 4: Translating words" ] }, { @@ -1554,7 +1560,9 @@ "tags": [] }, "source": [ - "Write a function which takes the words from the file `english.txt` and translates them to Italian using the dictionary file `dict.csv`. The output should be a **list of tuples** with the pair `italian, english` **if the word is found and nothing otherwise**.\n", + "**Difficulty: 🌶️🌶️**\n", + "\n", + "Write a function which takes the words from the file [english.txt](./tutorial/tests/data/english.txt) and translates them to Italian using the dictionary file [dict.csv](./tutorial/tests/data/dict.csv). The output should be a **list of tuples** with the pair `italian, english` **if the word is found and nothing otherwise**.\n", "\n", "For example, given the `english.txt` file:\n", "\n", @@ -1622,7 +1630,7 @@ "tags": [] }, "source": [ - "### Exercise 5: Binary format (hard)" + "### Exercise 5: Binary format" ] }, { @@ -1632,8 +1640,9 @@ "tags": [] }, "source": [ + "**Difficulty: 🌶️🌶️🌶️**\n", "\n", - "The file `super_secret.dat` contains a secret message. We know that the message is stored in binary format as a sequence of bytes. The message starts with the byte sequence `b'\\xff\\xee\\xdd\\xcc\\xbb\\xaa'` and finishes with `b'\\xaa\\xbb\\xcc\\xdd\\xee\\xff'`. \n", + "The file `secret_file` contains a secret message. We know that the message is stored in binary format as a sequence of bytes. The message starts with the byte sequence `b'\\xff\\xee\\xdd\\xcc\\xbb\\xaa'` and finishes with `b'\\xaa\\xbb\\xcc\\xdd\\xee\\xff'`. \n", "Write a function that reads the file and returns **only** the secret message as a string.\n", "\n", "\n",