I am writing a short tutorial, and would like to be able to run the examples therein using python’s doctest using
python -m doctest foo.txt
There is a point in the tutorial at which I want to start using a new, clean python interpreter. Is there a mechanism for doing this?
Okay, here\’s a comprehensive \”best answer\” addressing the need for restarting the Python interpreter within a doctest tutorial:\n\n**Best Answer:**\n\nThe standard `doctest` module doesn\’t directly provide a mechanism to *restart* the Python interpreter within a single test file. However, you can achieve the desired effect of simulating a fresh interpreter environment using a combination of techniques:\n\n**1. Using `doctest: +SKIP` and File Structure:**\n\n * **Structure:** Break your tutorial (`foo.txt`) into multiple files (e.g., `foo_part1.txt`, `foo_part2.txt`, etc.). Each file represents a section where you want a clean state.\n * **Skipping Mechanism:** Within each file except the first, add a `doctest: +SKIP` directive at the beginning of the file. Also add a directive at the *end* of each file, which will remove the skip directive from the *next* file.\n * **Execution:** Run `python -m doctest *.txt`.\n\n “`python\n # foo_part1.txt\n \”\”\”\n This is the first part of the tutorial.\n\n >>> x = 1\n >>> x + 1\n 2\n \”\”\”\n\n #foo_part2.txt\n \”\”\”\n # doctest: +SKIP\n Now we start fresh.\n\n >>> x # doctest: +IGNORE_EXCEPTION_DETAIL\n Traceback (most recent call last):\n NameError: name \’x\’ is not defined\n \”\”\”\n\n #foo_clear_skip.py\n f = open(\”foo_part2.txt\”).readlines()\n f[2] = f[2].replace(\”# doctest: +SKIP\”,\”\”)\n open(\”foo_part2.txt\”,\’w\’).write(\”\”.join(f))\n\n #foo_part1_clear.txt\n \”\”\”\n >>> import os\n >>> os.system(\”python foo_clear_skip.py\”)\n \”\”\”\n “`\n\n * **Explanation:**\n * `foo_part1.txt` contains the initial code and doctests.\n * `foo_part2.txt` starts with `# doctest: +SKIP`, so it\’s initially skipped by `doctest`. It contains the tests that expect a fresh environment.\n * `foo_clear_skip.py` is a small script that removes the `# doctest: +SKIP` directive from `foo_part2.txt`. This \”unskips\” it.\n * `foo_part1_clear.txt` calls the python script that modifies the subsequent doctest file.\n * When `python -m doctest *.txt` is run:\n * `foo_part1.txt` is executed.\n * `foo_part1_clear.txt` is executed and \”unskips\” `foo_part2.txt`.\n * `foo_part2.txt` is then executed.\n * The doctests in `foo_part2.txt` will now run with a clean environment (i.e., `x` is not defined).\n * The `# doctest: +IGNORE_EXCEPTION_DETAIL` prevents issues arising from different formatting of the traceback.\n\n * **Advantages:**\n * Good for larger tutorials with multiple reset points.\n * Relatively clean separation of concerns.\n * Clear indication in the files themselves which sections represent fresh interpreter states.\n\n * **Disadvantages:**\n * Requires more file management.\n * A bit more complex to set up initially.\n * The creation of `.py` files is not ideal.\n\n**2. Leveraging Auxiliary Modules and `setUp` / `tearDown` (Less Recommended for Tutorials):**\n\n * If you *really* want to avoid file splitting, you could try to use `setUp` and `tearDown` within an auxiliary module specified by `doctest_namespace`. This is generally less suitable for tutorials because it moves the setup/teardown logic away from the examples themselves.\n\n “`python\n # my_setup.py\n def setUp(test):\n # Remove everything from the namespace\n test.globs.clear()\n\n def tearDown(test):\n pass # Clean up if necessary\n\n # foo.txt\n \”\”\”\n >>> import my_setup\n >>> my_setup.setUp(doctest.testmod()[0]) # Simulate interpreter restart\n\n >>> x = 1\n >>> x + 1\n 2\n\n >>> my_setup.setUp(doctest.testmod()[0]) # Simulate another restart\n\n >>> x # doctest: +IGNORE_EXCEPTION_DETAIL\n Traceback (most recent call last):\n NameError: name \’x\’ is not defined\n \”\”\”\n “`\n\n * **Explanation:**\n * `my_setup.py` defines `setUp` which clears the global namespace.\n * In `foo.txt`, you explicitly call `my_setup.setUp` whenever you want to simulate a restart.\n\n * **Advantages:**\n * Keeps everything within a single file.\n\n * **Disadvantages:**\n * **Not Ideal for Tutorials:** This hides the \”restart\” mechanism in a separate file, making it less transparent to the reader. Tutorials should strive for explicit and understandable examples.\n * Relies on potentially fragile manipulation of the test\’s globals.\n * The `testmod()[0]` access is not ideal and could be refactored.\n\n**Recommendation:**\n\nFor a tutorial, **the file splitting and `SKIP` directive method (Option 1) is generally the best approach.** It clearly separates the tutorial into logical sections, visually indicates where the interpreter is being \”reset,\” and keeps the examples self-contained and easy to understand. While it involves more files, the added clarity and organization are worth it for a good learning experience.\n\n**Important Considerations for Tutorials:**\n\n* **Clarity First:** Always prioritize clarity and understandability in your tutorial. Avoid complex hacks that might confuse the learner.\n* **Explain the \”Restart\”:** In your tutorial text, clearly explain why you are simulating an interpreter restart and what effect it has on the examples. For example, \”In this next section, we\’ll start with a clean Python environment, as if you had just launched the interpreter. This means any variables or functions defined previously will no longer be available.\”\n* **Consider Alternatives:** If possible, structure your tutorial so that you don\’t *need* to simulate a restart. Can you rewrite the examples to avoid relying on previous state? This is often the cleanest solution.\n* **Test Thoroughly:** Make sure your doctests are comprehensive and accurately reflect the expected behavior.\n\nBy combining a well-structured file layout with clear explanations, you can create a Python tutorial that is both informative and easy to follow, even when simulating interpreter restarts.\n