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.
@@ -2659,12 +2663,52 @@ def test_testfile(): r"""
26592663 >>> sys.argv = save_argv
26602664"""
26612665
2666+ class TestImporter (importlib .abc .MetaPathFinder , importlib .abc .ResourceLoader ):
2667+
2668+ def find_spec (self , fullname , path , target = None ):
2669+ return importlib .util .spec_from_file_location (fullname , path , loader = self )
2670+
2671+ def get_data (self , path ):
2672+ with open (path , mode = 'rb' ) as f :
2673+ return f .read ()
2674+
2675+ class TestHook :
2676+
2677+ def __init__ (self , pathdir ):
2678+ self .sys_path = sys .path [:]
2679+ self .meta_path = sys .meta_path [:]
2680+ self .path_hooks = sys .path_hooks [:]
2681+ sys .path .append (pathdir )
2682+ sys .path_importer_cache .clear ()
2683+ self .modules_before = sys .modules .copy ()
2684+ self .importer = TestImporter ()
2685+ sys .meta_path .append (self .importer )
2686+
2687+ def remove (self ):
2688+ sys .path [:] = self .sys_path
2689+ sys .meta_path [:] = self .meta_path
2690+ sys .path_hooks [:] = self .path_hooks
2691+ sys .path_importer_cache .clear ()
2692+ sys .modules .clear ()
2693+ sys .modules .update (self .modules_before )
2694+
2695+
2696+ @contextlib .contextmanager
2697+ def test_hook (pathdir ):
2698+ hook = TestHook (pathdir )
2699+ try :
2700+ yield hook
2701+ finally :
2702+ hook .remove ()
2703+
2704+
26622705def test_lineendings (): r"""
2663- *nix systems use \n line endings, while Windows systems use \r\n. Python
2706+ *nix systems use \n line endings, while Windows systems use \r\n, and
2707+ old Mac systems used \r, which Python still recognizes as a line ending. Python
26642708handles this using universal newline mode for reading files. Let's make
26652709sure doctest does so (issue 8473) by creating temporary test files using each
2666- of the two line disciplines. One of the two will be the "wrong" one for the
2667- platform the test is run on.
2710+ of the three line disciplines. At least one will not match either the universal
2711+ newline \n or os.linesep for the platform the test is run on.
26682712
26692713Windows line endings first:
26702714
@@ -2687,6 +2731,47 @@ def test_lineendings(): r"""
26872731 TestResults(failed=0, attempted=1)
26882732 >>> os.remove(fn)
26892733
2734+ And finally old Mac line endings:
2735+
2736+ >>> fn = tempfile.mktemp()
2737+ >>> with open(fn, 'wb') as f:
2738+ ... f.write(b'Test:\r\r >>> x = 1 + 1\r\rDone.\r')
2739+ 30
2740+ >>> doctest.testfile(fn, module_relative=False, verbose=False)
2741+ TestResults(failed=0, attempted=1)
2742+ >>> os.remove(fn)
2743+
2744+ Now we test with a package loader that has a get_data method, since that
2745+ bypasses the standard universal newline handling so doctest has to do the
2746+ newline conversion itself; let's make sure it does so correctly (issue 1812).
2747+ We'll write a file inside the package that has all three kinds of line endings
2748+ in it, and use a package hook to install a custom loader; on any platform,
2749+ at least one of the line endings will raise a ValueError for inconsistent
2750+ whitespace if doctest does not correctly do the newline conversion.
2751+
2752+ >>> dn = tempfile.mkdtemp()
2753+ >>> pkg = os.path.join(dn, "doctest_testpkg")
2754+ >>> os.mkdir(pkg)
2755+ >>> support.create_empty_file(os.path.join(pkg, "__init__.py"))
2756+ >>> fn = os.path.join(pkg, "doctest_testfile.txt")
2757+ >>> with open(fn, 'wb') as f:
2758+ ... f.write(
2759+ ... b'Test:\r\n\r\n'
2760+ ... b' >>> x = 1 + 1\r\n\r\n'
2761+ ... b'Done.\r\n'
2762+ ... b'Test:\n\n'
2763+ ... b' >>> x = 1 + 1\n\n'
2764+ ... b'Done.\n'
2765+ ... b'Test:\r\r'
2766+ ... b' >>> x = 1 + 1\r\r'
2767+ ... b'Done.\r'
2768+ ... )
2769+ 95
2770+ >>> with test_hook(dn):
2771+ ... doctest.testfile("doctest_testfile.txt", package="doctest_testpkg", verbose=False)
2772+ TestResults(failed=0, attempted=3)
2773+ >>> shutil.rmtree(dn)
2774+
26902775"""
26912776
26922777def test_testmod (): r"""
0 commit comments