|
| 1 | +# This test module covers support in various parts of the standard library |
| 2 | +# for working with modules located inside zipfiles |
| 3 | +# The tests are centralised in this fashion to make it easy to drop them |
| 4 | +# if a platform doesn't support zipimport |
| 5 | +import test.support |
| 6 | +import os |
| 7 | +import os.path |
| 8 | +import sys |
| 9 | +import textwrap |
| 10 | +import zipfile |
| 11 | +import zipimport |
| 12 | +import doctest |
| 13 | +import inspect |
| 14 | +import linecache |
| 15 | +import unittest |
| 16 | +from test.support import os_helper |
| 17 | +from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, |
| 18 | + make_script, make_zip_script) |
| 19 | + |
| 20 | +verbose = test.support.verbose |
| 21 | + |
| 22 | +# Library modules covered by this test set |
| 23 | +# pdb (Issue 4201) |
| 24 | +# inspect (Issue 4223) |
| 25 | +# doctest (Issue 4197) |
| 26 | + |
| 27 | +# Other test modules with zipimport related tests |
| 28 | +# test_zipimport (of course!) |
| 29 | +# test_cmd_line_script (covers the zipimport support in runpy) |
| 30 | + |
| 31 | +# Retrieve some helpers from other test cases |
| 32 | +from test.test_doctest import (test_doctest, |
| 33 | + sample_doctest, sample_doctest_no_doctests, |
| 34 | + sample_doctest_no_docstrings, sample_doctest_skip) |
| 35 | + |
| 36 | + |
| 37 | +def _run_object_doctest(obj, module): |
| 38 | + from test.support.rustpython import DocTestChecker # TODO: RUSTPYTHON |
| 39 | + finder = doctest.DocTestFinder(verbose=verbose, recurse=False) |
| 40 | + # TODO: RUSTPYTHON |
| 41 | + # runner = doctest.DocTestRunner(verbose=verbose) |
| 42 | + runner = doctest.DocTestRunner(verbose=verbose, checker=DocTestChecker()) |
| 43 | + # Use the object's fully qualified name if it has one |
| 44 | + # Otherwise, use the module's name |
| 45 | + try: |
| 46 | + name = "%s.%s" % (obj.__module__, obj.__qualname__) |
| 47 | + except AttributeError: |
| 48 | + name = module.__name__ |
| 49 | + for example in finder.find(obj, name, module): |
| 50 | + runner.run(example) |
| 51 | + f, t = runner.failures, runner.tries |
| 52 | + if f: |
| 53 | + raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) |
| 54 | + if verbose: |
| 55 | + print ('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) |
| 56 | + return f, t |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +class ZipSupportTests(unittest.TestCase): |
| 61 | + # This used to use the ImportHooksBaseTestCase to restore |
| 62 | + # the state of the import related information |
| 63 | + # in the sys module after each test. However, that restores |
| 64 | + # *too much* information and breaks for the invocation |
| 65 | + # of test_doctest. So we do our own thing and leave |
| 66 | + # sys.modules alone. |
| 67 | + # We also clear the linecache and zipimport cache |
| 68 | + # just to avoid any bogus errors due to name reuse in the tests |
| 69 | + def setUp(self): |
| 70 | + linecache.clearcache() |
| 71 | + zipimport._zip_directory_cache.clear() |
| 72 | + self.path = sys.path[:] |
| 73 | + self.meta_path = sys.meta_path[:] |
| 74 | + self.path_hooks = sys.path_hooks[:] |
| 75 | + sys.path_importer_cache.clear() |
| 76 | + |
| 77 | + def tearDown(self): |
| 78 | + sys.path[:] = self.path |
| 79 | + sys.meta_path[:] = self.meta_path |
| 80 | + sys.path_hooks[:] = self.path_hooks |
| 81 | + sys.path_importer_cache.clear() |
| 82 | + |
| 83 | + def test_inspect_getsource_issue4223(self): |
| 84 | + test_src = "def foo(): pass\n" |
| 85 | + with os_helper.temp_dir() as d: |
| 86 | + init_name = make_script(d, '__init__', test_src) |
| 87 | + name_in_zip = os.path.join('zip_pkg', |
| 88 | + os.path.basename(init_name)) |
| 89 | + zip_name, run_name = make_zip_script(d, 'test_zip', |
| 90 | + init_name, name_in_zip) |
| 91 | + os.remove(init_name) |
| 92 | + sys.path.insert(0, zip_name) |
| 93 | + import zip_pkg |
| 94 | + try: |
| 95 | + self.assertEqual(inspect.getsource(zip_pkg.foo), test_src) |
| 96 | + finally: |
| 97 | + del sys.modules["zip_pkg"] |
| 98 | + |
| 99 | + def test_doctest_issue4197(self): |
| 100 | + # To avoid having to keep two copies of the doctest module's |
| 101 | + # unit tests in sync, this test works by taking the source of |
| 102 | + # test_doctest itself, rewriting it a bit to cope with a new |
| 103 | + # location, and then throwing it in a zip file to make sure |
| 104 | + # everything still works correctly |
| 105 | + test_src = inspect.getsource(test_doctest) |
| 106 | + test_src = test_src.replace( |
| 107 | + "from test.test_doctest import test_doctest", |
| 108 | + "import test_zipped_doctest as test_doctest") |
| 109 | + test_src = test_src.replace("test.test_doctest.test_doctest", |
| 110 | + "test_zipped_doctest") |
| 111 | + test_src = test_src.replace("test.test_doctest.sample_doctest", |
| 112 | + "sample_zipped_doctest") |
| 113 | + # The sample doctest files rewritten to include in the zipped version. |
| 114 | + sample_sources = {} |
| 115 | + for mod in [sample_doctest, sample_doctest_no_doctests, |
| 116 | + sample_doctest_no_docstrings, sample_doctest_skip]: |
| 117 | + src = inspect.getsource(mod) |
| 118 | + src = src.replace("test.test_doctest.test_doctest", "test_zipped_doctest") |
| 119 | + # Rewrite the module name so that, for example, |
| 120 | + # "test.sample_doctest" becomes "sample_zipped_doctest". |
| 121 | + mod_name = mod.__name__.split(".")[-1] |
| 122 | + mod_name = mod_name.replace("sample_", "sample_zipped_") |
| 123 | + sample_sources[mod_name] = src |
| 124 | + |
| 125 | + with os_helper.temp_dir() as d: |
| 126 | + script_name = make_script(d, 'test_zipped_doctest', |
| 127 | + test_src) |
| 128 | + zip_name, run_name = make_zip_script(d, 'test_zip', |
| 129 | + script_name) |
| 130 | + with zipfile.ZipFile(zip_name, 'a') as z: |
| 131 | + for mod_name, src in sample_sources.items(): |
| 132 | + z.writestr(mod_name + ".py", src) |
| 133 | + if verbose: |
| 134 | + with zipfile.ZipFile(zip_name, 'r') as zip_file: |
| 135 | + print ('Contents of %r:' % zip_name) |
| 136 | + zip_file.printdir() |
| 137 | + os.remove(script_name) |
| 138 | + sys.path.insert(0, zip_name) |
| 139 | + import test_zipped_doctest |
| 140 | + try: |
| 141 | + # Some of the doc tests depend on the colocated text files |
| 142 | + # which aren't available to the zipped version (the doctest |
| 143 | + # module currently requires real filenames for non-embedded |
| 144 | + # tests). So we're forced to be selective about which tests |
| 145 | + # to run. |
| 146 | + # doctest could really use some APIs which take a text |
| 147 | + # string or a file object instead of a filename... |
| 148 | + known_good_tests = [ |
| 149 | + test_zipped_doctest.SampleClass, |
| 150 | + test_zipped_doctest.SampleClass.NestedClass, |
| 151 | + test_zipped_doctest.SampleClass.NestedClass.__init__, |
| 152 | + test_zipped_doctest.SampleClass.__init__, |
| 153 | + test_zipped_doctest.SampleClass.a_classmethod, |
| 154 | + test_zipped_doctest.SampleClass.a_property, |
| 155 | + test_zipped_doctest.SampleClass.a_staticmethod, |
| 156 | + test_zipped_doctest.SampleClass.double, |
| 157 | + test_zipped_doctest.SampleClass.get, |
| 158 | + test_zipped_doctest.SampleNewStyleClass, |
| 159 | + test_zipped_doctest.SampleNewStyleClass.__init__, |
| 160 | + test_zipped_doctest.SampleNewStyleClass.double, |
| 161 | + test_zipped_doctest.SampleNewStyleClass.get, |
| 162 | + test_zipped_doctest.sample_func, |
| 163 | + test_zipped_doctest.test_DocTest, |
| 164 | + test_zipped_doctest.test_DocTestParser, |
| 165 | + test_zipped_doctest.test_DocTestRunner.basics, |
| 166 | + test_zipped_doctest.test_DocTestRunner.exceptions, |
| 167 | + test_zipped_doctest.test_DocTestRunner.option_directives, |
| 168 | + test_zipped_doctest.test_DocTestRunner.optionflags, |
| 169 | + test_zipped_doctest.test_DocTestRunner.verbose_flag, |
| 170 | + test_zipped_doctest.test_Example, |
| 171 | + test_zipped_doctest.test_debug, |
| 172 | + test_zipped_doctest.test_testsource, |
| 173 | + test_zipped_doctest.test_trailing_space_in_test, |
| 174 | + test_zipped_doctest.test_DocTestSuite, |
| 175 | + test_zipped_doctest.test_DocTestFinder, |
| 176 | + ] |
| 177 | + # These tests are the ones which need access |
| 178 | + # to the data files, so we don't run them |
| 179 | + fail_due_to_missing_data_files = [ |
| 180 | + test_zipped_doctest.test_DocFileSuite, |
| 181 | + test_zipped_doctest.test_testfile, |
| 182 | + test_zipped_doctest.test_unittest_reportflags, |
| 183 | + ] |
| 184 | + |
| 185 | + for obj in known_good_tests: |
| 186 | + _run_object_doctest(obj, test_zipped_doctest) |
| 187 | + finally: |
| 188 | + del sys.modules["test_zipped_doctest"] |
| 189 | + |
| 190 | + def test_doctest_main_issue4197(self): |
| 191 | + test_src = textwrap.dedent("""\ |
| 192 | + class Test: |
| 193 | + ">>> 'line 2'" |
| 194 | + pass |
| 195 | +
|
| 196 | + import doctest |
| 197 | + doctest.testmod() |
| 198 | + """) |
| 199 | + pattern = 'File "%s", line 2, in %s' |
| 200 | + with os_helper.temp_dir() as d: |
| 201 | + script_name = make_script(d, 'script', test_src) |
| 202 | + rc, out, err = assert_python_ok(script_name) |
| 203 | + expected = pattern % (script_name, "__main__.Test") |
| 204 | + if verbose: |
| 205 | + print ("Expected line", expected) |
| 206 | + print ("Got stdout:") |
| 207 | + print (ascii(out)) |
| 208 | + self.assertIn(expected.encode('utf-8'), out) |
| 209 | + zip_name, run_name = make_zip_script(d, "test_zip", |
| 210 | + script_name, '__main__.py') |
| 211 | + rc, out, err = assert_python_ok(zip_name) |
| 212 | + expected = pattern % (run_name, "__main__.Test") |
| 213 | + if verbose: |
| 214 | + print ("Expected line", expected) |
| 215 | + print ("Got stdout:") |
| 216 | + print (ascii(out)) |
| 217 | + self.assertIn(expected.encode('utf-8'), out) |
| 218 | + |
| 219 | + def test_pdb_issue4201(self): |
| 220 | + test_src = textwrap.dedent("""\ |
| 221 | + def f(): |
| 222 | + pass |
| 223 | +
|
| 224 | + import pdb |
| 225 | + pdb.Pdb(nosigint=True).runcall(f) |
| 226 | + """) |
| 227 | + with os_helper.temp_dir() as d: |
| 228 | + script_name = make_script(d, 'script', test_src) |
| 229 | + p = spawn_python(script_name) |
| 230 | + p.stdin.write(b'l\n') |
| 231 | + data = kill_python(p) |
| 232 | + # bdb/pdb applies normcase to its filename before displaying |
| 233 | + self.assertIn(os.path.normcase(script_name.encode('utf-8')), data) |
| 234 | + zip_name, run_name = make_zip_script(d, "test_zip", |
| 235 | + script_name, '__main__.py') |
| 236 | + p = spawn_python(zip_name) |
| 237 | + p.stdin.write(b'l\n') |
| 238 | + data = kill_python(p) |
| 239 | + # bdb/pdb applies normcase to its filename before displaying |
| 240 | + self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) |
| 241 | + |
| 242 | + |
| 243 | +def tearDownModule(): |
| 244 | + test.support.reap_children() |
| 245 | + |
| 246 | +if __name__ == '__main__': |
| 247 | + unittest.main() |
0 commit comments