88import os
99import sys
1010import importlib
11+ import importlib .abc
12+ import importlib .util
1113import unittest
1214import tempfile
15+ import shutil
16+ import contextlib
1317
1418# NOTE: There are some additional tests relating to interaction with
1519# zipimport in the test_zipimport_support test module.
@@ -437,7 +441,7 @@ def basics(): r"""
437441 >>> tests = finder.find(sample_func)
438442
439443 >>> print(tests) # doctest: +ELLIPSIS
440- [<DocTest sample_func from ...:21 (1 example)>]
444+ [<DocTest sample_func from ...:25 (1 example)>]
441445
442446The exact name depends on how test_doctest was invoked, so allow for
443447leading path components.
@@ -2663,12 +2667,52 @@ def test_testfile(): r"""
26632667 >>> sys.argv = save_argv
26642668"""
26652669
2670+ class TestImporter (importlib .abc .MetaPathFinder , importlib .abc .ResourceLoader ):
2671+
2672+ def find_spec (self , fullname , path , target = None ):
2673+ return importlib .util .spec_from_file_location (fullname , path , loader = self )
2674+
2675+ def get_data (self , path ):
2676+ with open (path , mode = 'rb' ) as f :
2677+ return f .read ()
2678+
2679+ class TestHook :
2680+
2681+ def __init__ (self , pathdir ):
2682+ self .sys_path = sys .path [:]
2683+ self .meta_path = sys .meta_path [:]
2684+ self .path_hooks = sys .path_hooks [:]
2685+ sys .path .append (pathdir )
2686+ sys .path_importer_cache .clear ()
2687+ self .modules_before = sys .modules .copy ()
2688+ self .importer = TestImporter ()
2689+ sys .meta_path .append (self .importer )
2690+
2691+ def remove (self ):
2692+ sys .path [:] = self .sys_path
2693+ sys .meta_path [:] = self .meta_path
2694+ sys .path_hooks [:] = self .path_hooks
2695+ sys .path_importer_cache .clear ()
2696+ sys .modules .clear ()
2697+ sys .modules .update (self .modules_before )
2698+
2699+
2700+ @contextlib .contextmanager
2701+ def test_hook (pathdir ):
2702+ hook = TestHook (pathdir )
2703+ try :
2704+ yield hook
2705+ finally :
2706+ hook .remove ()
2707+
2708+
26662709def test_lineendings (): r"""
2667- *nix systems use \n line endings, while Windows systems use \r\n. Python
2710+ *nix systems use \n line endings, while Windows systems use \r\n, and
2711+ old Mac systems used \r, which Python still recognizes as a line ending. Python
26682712handles this using universal newline mode for reading files. Let's make
26692713sure doctest does so (issue 8473) by creating temporary test files using each
2670- of the two line disciplines. One of the two will be the "wrong" one for the
2671- platform the test is run on.
2714+ of the three line disciplines. At least one will not match either the universal
2715+ newline \n or os.linesep for the platform the test is run on.
26722716
26732717Windows line endings first:
26742718
@@ -2691,6 +2735,47 @@ def test_lineendings(): r"""
26912735 TestResults(failed=0, attempted=1)
26922736 >>> os.remove(fn)
26932737
2738+ And finally old Mac line endings:
2739+
2740+ >>> fn = tempfile.mktemp()
2741+ >>> with open(fn, 'wb') as f:
2742+ ... f.write(b'Test:\r\r >>> x = 1 + 1\r\rDone.\r')
2743+ 30
2744+ >>> doctest.testfile(fn, module_relative=False, verbose=False)
2745+ TestResults(failed=0, attempted=1)
2746+ >>> os.remove(fn)
2747+
2748+ Now we test with a package loader that has a get_data method, since that
2749+ bypasses the standard universal newline handling so doctest has to do the
2750+ newline conversion itself; let's make sure it does so correctly (issue 1812).
2751+ We'll write a file inside the package that has all three kinds of line endings
2752+ in it, and use a package hook to install a custom loader; on any platform,
2753+ at least one of the line endings will raise a ValueError for inconsistent
2754+ whitespace if doctest does not correctly do the newline conversion.
2755+
2756+ >>> dn = tempfile.mkdtemp()
2757+ >>> pkg = os.path.join(dn, "doctest_testpkg")
2758+ >>> os.mkdir(pkg)
2759+ >>> support.create_empty_file(os.path.join(pkg, "__init__.py"))
2760+ >>> fn = os.path.join(pkg, "doctest_testfile.txt")
2761+ >>> with open(fn, 'wb') as f:
2762+ ... f.write(
2763+ ... b'Test:\r\n\r\n'
2764+ ... b' >>> x = 1 + 1\r\n\r\n'
2765+ ... b'Done.\r\n'
2766+ ... b'Test:\n\n'
2767+ ... b' >>> x = 1 + 1\n\n'
2768+ ... b'Done.\n'
2769+ ... b'Test:\r\r'
2770+ ... b' >>> x = 1 + 1\r\r'
2771+ ... b'Done.\r'
2772+ ... )
2773+ 95
2774+ >>> with test_hook(dn):
2775+ ... doctest.testfile("doctest_testfile.txt", package="doctest_testpkg", verbose=False)
2776+ TestResults(failed=0, attempted=3)
2777+ >>> shutil.rmtree(dn)
2778+
26942779"""
26952780
26962781def test_testmod (): r"""
0 commit comments