Merged revisions 76286-76287,76289-76294,76296-76299,76301-76305,76307,76310-76311,76313-76322 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r76286 | nick.coghlan | 2009-11-15 17:30:34 +1000 (Sun, 15 Nov 2009) | 1 line

  Issue #6816: expose the zipfile and directory execution mechanism to Python code via the runpy module. Also consolidated some script execution functionality in the test harness into a helper module and removed some implementation details from the runpy module documentation.
........
  r76321 | nick.coghlan | 2009-11-16 13:55:51 +1000 (Mon, 16 Nov 2009) | 1 line

  Account for another cache when hunting ref leaks
........
  r76322 | nick.coghlan | 2009-11-16 13:57:32 +1000 (Mon, 16 Nov 2009) | 1 line

  Allow for backslashes in file paths passed to the regex engine
........
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
index df8a7b0..d4f228a 100755
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -957,6 +957,12 @@
     fs = warnings.filters[:]
     ps = copyreg.dispatch_table.copy()
     pic = sys.path_importer_cache.copy()
+    try:
+        import zipimport
+    except ImportError:
+        zdc = None # Run unmodified on platforms without zipimport support
+    else:
+        zdc = zipimport._zip_directory_cache.copy()
     abcs = {}
     for abc in [getattr(_abcoll, a) for a in _abcoll.__all__]:
         if not isabstract(abc):
@@ -978,13 +984,13 @@
     print("beginning", repcount, "repetitions", file=sys.stderr)
     print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr)
     sys.stderr.flush()
-    dash_R_cleanup(fs, ps, pic, abcs)
+    dash_R_cleanup(fs, ps, pic, zdc, abcs)
     for i in range(repcount):
         rc = sys.gettotalrefcount()
         run_the_test()
         sys.stderr.write('.')
         sys.stderr.flush()
-        dash_R_cleanup(fs, ps, pic, abcs)
+        dash_R_cleanup(fs, ps, pic, zdc, abcs)
         if i >= nwarmup:
             deltas.append(sys.gettotalrefcount() - rc - 2)
     print(file=sys.stderr)
@@ -998,7 +1004,7 @@
         return True
     return False
 
-def dash_R_cleanup(fs, ps, pic, abcs):
+def dash_R_cleanup(fs, ps, pic, zdc, abcs):
     import gc, copyreg
     import _strptime, linecache
     import urllib.parse, urllib.request, mimetypes, doctest
@@ -1017,6 +1023,13 @@
     copyreg.dispatch_table.update(ps)
     sys.path_importer_cache.clear()
     sys.path_importer_cache.update(pic)
+    try:
+        import zipimport
+    except ImportError:
+        pass # Run unmodified on platforms without zipimport support
+    else:
+        zipimport._zip_directory_cache.clear()
+        zipimport._zip_directory_cache.update(zdc)
 
     # clear type cache
     sys._clear_type_cache()
diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py
new file mode 100644
index 0000000..af5252c
--- /dev/null
+++ b/Lib/test/script_helper.py
@@ -0,0 +1,119 @@
+# Common utility functions used by various script execution tests
+#  e.g. test_cmd_line, test_cmd_line_script and test_runpy
+
+import sys
+import os
+import os.path
+import tempfile
+import subprocess
+import py_compile
+import contextlib
+import shutil
+import zipfile
+
+# Executing the interpreter in a subprocess
+def python_exit_code(*args):
+    cmd_line = [sys.executable, '-E']
+    cmd_line.extend(args)
+    with open(os.devnull, 'w') as devnull:
+        return subprocess.call(cmd_line, stdout=devnull,
+                                stderr=subprocess.STDOUT)
+
+def spawn_python(*args):
+    cmd_line = [sys.executable, '-E']
+    cmd_line.extend(args)
+    return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
+                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+def kill_python(p):
+    p.stdin.close()
+    data = p.stdout.read()
+    p.stdout.close()
+    # try to cleanup the child so we don't appear to leak when running
+    # with regrtest -R.  This should be a no-op on Windows.
+    subprocess._cleanup()
+    return data
+
+def run_python(*args):
+    if __debug__:
+        p = spawn_python(*args)
+    else:
+        p = spawn_python('-O', *args)
+    stdout_data = kill_python(p)
+    return p.wait(), stdout_data
+
+# Script creation utilities
+@contextlib.contextmanager
+def temp_dir():
+    dirname = tempfile.mkdtemp()
+    dirname = os.path.realpath(dirname)
+    try:
+        yield dirname
+    finally:
+        shutil.rmtree(dirname)
+
+def make_script(script_dir, script_basename, source):
+    script_filename = script_basename+os.extsep+'py'
+    script_name = os.path.join(script_dir, script_filename)
+    script_file = open(script_name, 'w')
+    script_file.write(source)
+    script_file.close()
+    return script_name
+
+def compile_script(script_name):
+    py_compile.compile(script_name, doraise=True)
+    if __debug__:
+        compiled_name = script_name + 'c'
+    else:
+        compiled_name = script_name + 'o'
+    return compiled_name
+
+def make_zip_script(zip_dir, zip_basename, script_name, name_in_zip=None):
+    zip_filename = zip_basename+os.extsep+'zip'
+    zip_name = os.path.join(zip_dir, zip_filename)
+    zip_file = zipfile.ZipFile(zip_name, 'w')
+    if name_in_zip is None:
+        name_in_zip = os.path.basename(script_name)
+    zip_file.write(script_name, name_in_zip)
+    zip_file.close()
+    #if test.test_support.verbose:
+    #    zip_file = zipfile.ZipFile(zip_name, 'r')
+    #    print 'Contents of %r:' % zip_name
+    #    zip_file.printdir()
+    #    zip_file.close()
+    return zip_name, os.path.join(zip_name, name_in_zip)
+
+def make_pkg(pkg_dir):
+    os.mkdir(pkg_dir)
+    make_script(pkg_dir, '__init__', '')
+
+def make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
+                 source, depth=1, compiled=False):
+    unlink = []
+    init_name = make_script(zip_dir, '__init__', '')
+    unlink.append(init_name)
+    init_basename = os.path.basename(init_name)
+    script_name = make_script(zip_dir, script_basename, source)
+    unlink.append(script_name)
+    if compiled:
+        init_name = compile_script(init_name)
+        script_name = compile_script(script_name)
+        unlink.extend((init_name, script_name))
+    pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)]
+    script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name))
+    zip_filename = zip_basename+os.extsep+'zip'
+    zip_name = os.path.join(zip_dir, zip_filename)
+    zip_file = zipfile.ZipFile(zip_name, 'w')
+    for name in pkg_names:
+        init_name_in_zip = os.path.join(name, init_basename)
+        zip_file.write(init_name, init_name_in_zip)
+    zip_file.write(script_name, script_name_in_zip)
+    zip_file.close()
+    for name in unlink:
+        os.unlink(name)
+    #if test.test_support.verbose:
+    #    zip_file = zipfile.ZipFile(zip_name, 'r')
+    #    print 'Contents of %r:' % zip_name
+    #    zip_file.printdir()
+    #    zip_file.close()
+    return zip_name, os.path.join(zip_name, script_name_in_zip)
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index b4a1f9f..852a7aa 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -6,44 +6,41 @@
 import test.support, unittest
 import os
 import sys
-import subprocess
+from test.script_helper import spawn_python, kill_python, python_exit_code
 
-def _spawn_python(*args):
+# XXX (ncoghlan): there are assorted gratuitous inconsistencies between the
+# support code in the Py3k version and the 2.x version that unnecessarily
+# complicate test suite merges. See issue 7331
+
+# spawn_python normally enforces use of -E to avoid environmental effects
+# but one test checks PYTHONPATH behaviour explicitly
+# XXX (ncoghlan): Give script_helper.spawn_python an option to switch
+# off the -E flag that is normally inserted automatically
+import subprocess
+def _spawn_python_with_env(*args):
     cmd_line = [sys.executable]
-    # When testing -S, we need PYTHONPATH to work (see test_site_flag())
-    if '-S' not in args:
-        cmd_line.append('-E')
     cmd_line.extend(args)
     return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
-def _kill_python(p):
-    return _kill_python_and_exit_code(p)[0]
 
+# XXX (ncoghlan): Move to script_helper and make consistent with run_python
 def _kill_python_and_exit_code(p):
-    p.stdin.close()
-    data = p.stdout.read()
-    p.stdout.close()
-    # try to cleanup the child so we don't appear to leak when running
-    # with regrtest -R.  This should be a no-op on Windows.
-    subprocess._cleanup()
+    data = kill_python(p)
     returncode = p.wait()
     return data, returncode
 
 class CmdLineTest(unittest.TestCase):
     def start_python(self, *args):
-        return self.start_python_and_exit_code(*args)[0]
+        p = spawn_python(*args)
+        return kill_python(p)
 
     def start_python_and_exit_code(self, *args):
-        p = _spawn_python(*args)
+        p = spawn_python(*args)
         return _kill_python_and_exit_code(p)
 
     def exit_code(self, *args):
-        cmd_line = [sys.executable, '-E']
-        cmd_line.extend(args)
-        with open(os.devnull, 'w') as devnull:
-            return subprocess.call(cmd_line, stdout=devnull,
-                                   stderr=subprocess.STDOUT)
+        return python_exit_code(*args)
 
     def test_directories(self):
         self.assertNotEqual(self.exit_code('.'), 0)
@@ -107,10 +104,10 @@
         # -m and -i need to play well together
         # Runs the timeit module and checks the __main__
         # namespace has been populated appropriately
-        p = _spawn_python('-i', '-m', 'timeit', '-n', '1')
+        p = spawn_python('-i', '-m', 'timeit', '-n', '1')
         p.stdin.write(b'Timer\n')
         p.stdin.write(b'exit()\n')
-        data = _kill_python(p)
+        data = kill_python(p)
         self.assertTrue(data.find(b'1 loop') != -1)
         self.assertTrue(data.find(b'__main__.Timer') != -1)
 
@@ -154,7 +151,7 @@
     def test_unbuffered_input(self):
         # sys.stdin still works with '-u'
         code = ("import sys; sys.stdout.write(sys.stdin.read(1))")
-        p = _spawn_python('-u', '-c', code)
+        p = spawn_python('-u', '-c', code)
         p.stdin.write(b'x')
         p.stdin.flush()
         data, rc = _kill_python_and_exit_code(p)
@@ -166,7 +163,8 @@
             path1 = "ABCDE" * 100
             path2 = "FGHIJ" * 100
             env['PYTHONPATH'] = path1 + os.pathsep + path2
-            p = _spawn_python('-S', '-c', 'import sys; print(sys.path)')
+            p = _spawn_python_with_env('-S', '-c',
+                                       'import sys; print(sys.path)')
             stdout, _ = p.communicate()
             self.assertTrue(path1.encode('ascii') in stdout)
             self.assertTrue(path2.encode('ascii') in stdout)
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 7114681..e6d6ffb 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -5,35 +5,12 @@
 import os.path
 import sys
 import test.support
-import tempfile
-import subprocess
-import py_compile
-import contextlib
-import shutil
-import zipfile
+from test.script_helper import (spawn_python, kill_python, run_python,
+                                temp_dir, make_script, compile_script,
+                                make_pkg, make_zip_script, make_zip_pkg)
 
 verbose = test.support.verbose
 
-# XXX ncoghlan: Should we consider moving these to support?
-from test.test_cmd_line import _spawn_python, _kill_python
-
-def _run_python(*args):
-    if __debug__:
-        p = _spawn_python(*args)
-    else:
-        p = _spawn_python('-O', *args)
-    stdout_data = _kill_python(p)
-    return p.wait(), stdout_data.decode()
-
-@contextlib.contextmanager
-def temp_dir():
-    dirname = tempfile.mkdtemp()
-    dirname = os.path.realpath(dirname)
-    try:
-        yield dirname
-    finally:
-        shutil.rmtree(dirname)
-
 test_source = """\
 # Script may be run with optimisation enabled, so don't rely on assert
 # statements being executed
@@ -60,63 +37,12 @@
 """
 
 def _make_test_script(script_dir, script_basename, source=test_source):
-    script_filename = script_basename+os.path.extsep+'py'
-    script_name = os.path.join(script_dir, script_filename)
-    script_file = open(script_name, 'w')
-    script_file.write(source)
-    script_file.close()
-    return script_name
-
-def _compile_test_script(script_name):
-    py_compile.compile(script_name, doraise=True)
-    if __debug__:
-        compiled_name = script_name + 'c'
-    else:
-        compiled_name = script_name + 'o'
-    return compiled_name
-
-def _make_test_zip(zip_dir, zip_basename, script_name, name_in_zip=None):
-    zip_filename = zip_basename+os.path.extsep+"zip"
-    zip_name = os.path.join(zip_dir, zip_filename)
-    zip_file = zipfile.ZipFile(zip_name, 'w')
-    if name_in_zip is None:
-        name_in_zip = os.path.basename(script_name)
-    zip_file.write(script_name, name_in_zip)
-    zip_file.close()
-    #if verbose:
-    #    zip_file = zipfile.ZipFile(zip_name, 'r')
-    #    print("Contents of %r:" % zip_name)
-    #    zip_file.printdir()
-    #    zip_file.close()
-    return zip_name, os.path.join(zip_name, name_in_zip)
-
-def _make_test_pkg(pkg_dir):
-    os.mkdir(pkg_dir)
-    _make_test_script(pkg_dir, '__init__', '')
+    return make_script(script_dir, script_basename, source)
 
 def _make_test_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
                        source=test_source, depth=1):
-    init_name = _make_test_script(zip_dir, '__init__', '')
-    init_basename = os.path.basename(init_name)
-    script_name = _make_test_script(zip_dir, script_basename, source)
-    pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)]
-    script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name))
-    zip_filename = zip_basename+os.extsep+'zip'
-    zip_name = os.path.join(zip_dir, zip_filename)
-    zip_file = zipfile.ZipFile(zip_name, 'w')
-    for name in pkg_names:
-        init_name_in_zip = os.path.join(name, init_basename)
-        zip_file.write(init_name, init_name_in_zip)
-    zip_file.write(script_name, script_name_in_zip)
-    zip_file.close()
-    os.unlink(init_name)
-    os.unlink(script_name)
-    #if verbose:
-    #    zip_file = zipfile.ZipFile(zip_name, 'r')
-    #    print('Contents of %r:' % zip_name)
-    #    zip_file.printdir()
-    #    zip_file.close()
-    return zip_name, os.path.join(zip_name, script_name_in_zip)
+    return make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
+                        source, depth)
 
 # There's no easy way to pass the script directory in to get
 # -m to work (avoiding that is the whole point of making
@@ -134,14 +60,14 @@
     else:
         path = repr(path)
     source = launch_source % (path, module_name)
-    return _make_test_script(script_dir, script_basename, source)
+    return make_script(script_dir, script_basename, source)
 
 class CmdLineTest(unittest.TestCase):
     def _check_script(self, script_name, expected_file,
                             expected_argv0, expected_package,
                             *cmd_line_switches):
         run_args = cmd_line_switches + (script_name,)
-        exit_code, data = _run_python(*run_args)
+        exit_code, data = run_python(*run_args)
         if verbose:
             print("Output from test script %r:" % script_name)
             print(data)
@@ -154,19 +80,19 @@
             print(printed_file)
             print(printed_package)
             print(printed_argv0)
-        self.assertTrue(printed_file in data)
-        self.assertTrue(printed_package in data)
-        self.assertTrue(printed_argv0 in data)
+        self.assertTrue(printed_file.encode('utf-8') in data)
+        self.assertTrue(printed_package.encode('utf-8') in data)
+        self.assertTrue(printed_argv0.encode('utf-8') in data)
 
     def _check_import_error(self, script_name, expected_msg,
                             *cmd_line_switches):
         run_args = cmd_line_switches + (script_name,)
-        exit_code, data = _run_python(*run_args)
+        exit_code, data = run_python(*run_args)
         if verbose:
             print('Output from test script %r:' % script_name)
             print(data)
             print('Expected output: %r' % expected_msg)
-        self.assertTrue(expected_msg in data)
+        self.assertTrue(expected_msg.encode('utf-8') in data)
 
     def test_basic_script(self):
         with temp_dir() as script_dir:
@@ -176,7 +102,7 @@
     def test_script_compiled(self):
         with temp_dir() as script_dir:
             script_name = _make_test_script(script_dir, 'script')
-            compiled_name = _compile_test_script(script_name)
+            compiled_name = compile_script(script_name)
             os.remove(script_name)
             self._check_script(compiled_name, compiled_name, compiled_name, None)
 
@@ -188,39 +114,39 @@
     def test_directory_compiled(self):
         with temp_dir() as script_dir:
             script_name = _make_test_script(script_dir, '__main__')
-            compiled_name = _compile_test_script(script_name)
+            compiled_name = compile_script(script_name)
             os.remove(script_name)
             self._check_script(script_dir, compiled_name, script_dir, '')
 
     def test_directory_error(self):
         with temp_dir() as script_dir:
-            msg = "can't find '__main__.py' in %r" % script_dir
+            msg = "can't find '__main__' module in %r" % script_dir
             self._check_import_error(script_dir, msg)
 
     def test_zipfile(self):
         with temp_dir() as script_dir:
             script_name = _make_test_script(script_dir, '__main__')
-            zip_name, run_name = _make_test_zip(script_dir, 'test_zip', script_name)
+            zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
             self._check_script(zip_name, run_name, zip_name, '')
 
     def test_zipfile_compiled(self):
         with temp_dir() as script_dir:
             script_name = _make_test_script(script_dir, '__main__')
-            compiled_name = _compile_test_script(script_name)
-            zip_name, run_name = _make_test_zip(script_dir, 'test_zip', compiled_name)
+            compiled_name = compile_script(script_name)
+            zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
             self._check_script(zip_name, run_name, zip_name, '')
 
     def test_zipfile_error(self):
         with temp_dir() as script_dir:
             script_name = _make_test_script(script_dir, 'not_main')
-            zip_name, run_name = _make_test_zip(script_dir, 'test_zip', script_name)
-            msg = "can't find '__main__.py' in %r" % zip_name
+            zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
+            msg = "can't find '__main__' module in %r" % zip_name
             self._check_import_error(zip_name, msg)
 
     def test_module_in_package(self):
         with temp_dir() as script_dir:
             pkg_dir = os.path.join(script_dir, 'test_pkg')
-            _make_test_pkg(pkg_dir)
+            make_pkg(pkg_dir)
             script_name = _make_test_script(pkg_dir, 'script')
             launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg.script')
             self._check_script(launch_name, script_name, script_name, 'test_pkg')
@@ -240,7 +166,7 @@
     def test_package(self):
         with temp_dir() as script_dir:
             pkg_dir = os.path.join(script_dir, 'test_pkg')
-            _make_test_pkg(pkg_dir)
+            make_pkg(pkg_dir)
             script_name = _make_test_script(pkg_dir, '__main__')
             launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
             self._check_script(launch_name, script_name,
@@ -249,9 +175,9 @@
     def test_package_compiled(self):
         with temp_dir() as script_dir:
             pkg_dir = os.path.join(script_dir, 'test_pkg')
-            _make_test_pkg(pkg_dir)
+            make_pkg(pkg_dir)
             script_name = _make_test_script(pkg_dir, '__main__')
-            compiled_name = _compile_test_script(script_name)
+            compiled_name = compile_script(script_name)
             os.remove(script_name)
             launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
             self._check_script(launch_name, compiled_name,
@@ -260,7 +186,7 @@
     def test_package_error(self):
         with temp_dir() as script_dir:
             pkg_dir = os.path.join(script_dir, 'test_pkg')
-            _make_test_pkg(pkg_dir)
+            make_pkg(pkg_dir)
             msg = ("'test_pkg' is a package and cannot "
                    "be directly executed")
             launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
@@ -269,9 +195,9 @@
     def test_package_recursion(self):
         with temp_dir() as script_dir:
             pkg_dir = os.path.join(script_dir, 'test_pkg')
-            _make_test_pkg(pkg_dir)
+            make_pkg(pkg_dir)
             main_dir = os.path.join(pkg_dir, '__main__')
-            _make_test_pkg(main_dir)
+            make_pkg(main_dir)
             msg = ("Cannot use package as __main__ module; "
                    "'test_pkg' is a package and cannot "
                    "be directly executed")
diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py
index 43141e5..bd22814 100644
--- a/Lib/test/test_runpy.py
+++ b/Lib/test/test_runpy.py
@@ -5,8 +5,11 @@
 import sys
 import tempfile
 from test.support import verbose, run_unittest, forget
-from runpy import _run_code, _run_module_code, run_module
+from test.script_helper import (temp_dir, make_script, compile_script,
+                                make_pkg, make_zip_script, make_zip_pkg)
 
+
+from runpy import _run_code, _run_module_code, run_module, run_path
 # Note: This module can't safely test _run_module_as_main as it
 # runs its tests in the current process, which would mess with the
 # real __main__ module (usually test.regrtest)
@@ -15,6 +18,7 @@
 # Set up the test code and expected results
 
 class RunModuleCodeTest(unittest.TestCase):
+    """Unit tests for runpy._run_code and runpy._run_module_code"""
 
     expected_result = ["Top level assignment", "Lower level reference"]
     test_source = (
@@ -37,14 +41,14 @@
     def test_run_code(self):
         saved_argv0 = sys.argv[0]
         d = _run_code(self.test_source, {})
-        self.assertTrue(d["result"] == self.expected_result)
-        self.assertTrue(d["__name__"] is None)
-        self.assertTrue(d["__file__"] is None)
-        self.assertTrue(d["__loader__"] is None)
-        self.assertTrue(d["__package__"] is None)
-        self.assertTrue(d["run_argv0"] is saved_argv0)
-        self.assertTrue("run_name" not in d)
-        self.assertTrue(sys.argv[0] is saved_argv0)
+        self.assertEqual(d["result"], self.expected_result)
+        self.assertIs(d["__name__"], None)
+        self.assertIs(d["__file__"], None)
+        self.assertIs(d["__loader__"], None)
+        self.assertIs(d["__package__"], None)
+        self.assertIs(d["run_argv0"], saved_argv0)
+        self.assertNotIn("run_name", d)
+        self.assertIs(sys.argv[0], saved_argv0)
 
     def test_run_module_code(self):
         initial = object()
@@ -60,22 +64,23 @@
                               file,
                               loader,
                               package)
-        self.assertTrue("result" not in d1)
-        self.assertTrue(d2["initial"] is initial)
+        self.assertNotIn("result", d1)
+        self.assertIs(d2["initial"], initial)
         self.assertEqual(d2["result"], self.expected_result)
         self.assertEqual(d2["nested"]["x"], 1)
-        self.assertTrue(d2["__name__"] is name)
+        self.assertIs(d2["__name__"], name)
         self.assertTrue(d2["run_name_in_sys_modules"])
         self.assertTrue(d2["module_in_sys_modules"])
-        self.assertTrue(d2["__file__"] is file)
-        self.assertTrue(d2["run_argv0"] is file)
-        self.assertTrue(d2["__loader__"] is loader)
-        self.assertTrue(d2["__package__"] is package)
-        self.assertTrue(sys.argv[0] is saved_argv0)
-        self.assertTrue(name not in sys.modules)
+        self.assertIs(d2["__file__"], file)
+        self.assertIs(d2["run_argv0"], file)
+        self.assertIs(d2["__loader__"], loader)
+        self.assertIs(d2["__package__"], package)
+        self.assertIs(sys.argv[0], saved_argv0)
+        self.assertNotIn(name, sys.modules)
 
 
 class RunModuleTest(unittest.TestCase):
+    """Unit tests for runpy.run_module"""
 
     def expect_import_error(self, mod_name):
         try:
@@ -245,9 +250,126 @@
             self._check_relative_imports(depth, "__main__")
 
 
+class RunPathTest(unittest.TestCase):
+    """Unit tests for runpy.run_path"""
+    # Based on corresponding tests in test_cmd_line_script
+
+    test_source = """\
+# Script may be run with optimisation enabled, so don't rely on assert
+# statements being executed
+def assertEqual(lhs, rhs):
+    if lhs != rhs:
+        raise AssertionError('%r != %r' % (lhs, rhs))
+def assertIs(lhs, rhs):
+    if lhs is not rhs:
+        raise AssertionError('%r is not %r' % (lhs, rhs))
+# Check basic code execution
+result = ['Top level assignment']
+def f():
+    result.append('Lower level reference')
+f()
+assertEqual(result, ['Top level assignment', 'Lower level reference'])
+# Check the sys module
+import sys
+assertIs(globals(), sys.modules[__name__].__dict__)
+argv0 = sys.argv[0]
+"""
+
+    def _make_test_script(self, script_dir, script_basename, source=None):
+        if source is None:
+            source = self.test_source
+        return make_script(script_dir, script_basename, source)
+
+    def _check_script(self, script_name, expected_name, expected_file,
+                            expected_argv0, expected_package):
+        result = run_path(script_name)
+        self.assertEqual(result["__name__"], expected_name)
+        self.assertEqual(result["__file__"], expected_file)
+        self.assertIn("argv0", result)
+        self.assertEqual(result["argv0"], expected_argv0)
+        self.assertEqual(result["__package__"], expected_package)
+
+    def _check_import_error(self, script_name, msg):
+        # Double backslashes to handle path separators on Windows
+        msg = msg.replace("\\", "\\\\")
+        self.assertRaisesRegexp(ImportError, msg, run_path, script_name)
+
+    def test_basic_script(self):
+        with temp_dir() as script_dir:
+            mod_name = 'script'
+            script_name = self._make_test_script(script_dir, mod_name)
+            self._check_script(script_name, "<run_path>", script_name,
+                               script_name, None)
+
+    def test_script_compiled(self):
+        with temp_dir() as script_dir:
+            mod_name = 'script'
+            script_name = self._make_test_script(script_dir, mod_name)
+            compiled_name = compile_script(script_name)
+            os.remove(script_name)
+            self._check_script(compiled_name, "<run_path>", compiled_name,
+                               compiled_name, None)
+
+    def test_directory(self):
+        with temp_dir() as script_dir:
+            mod_name = '__main__'
+            script_name = self._make_test_script(script_dir, mod_name)
+            self._check_script(script_dir, "<run_path>", script_name,
+                               script_dir, '')
+
+    def test_directory_compiled(self):
+        with temp_dir() as script_dir:
+            mod_name = '__main__'
+            script_name = self._make_test_script(script_dir, mod_name)
+            compiled_name = compile_script(script_name)
+            os.remove(script_name)
+            self._check_script(script_dir, "<run_path>", compiled_name,
+                               script_dir, '')
+
+    def test_directory_error(self):
+        with temp_dir() as script_dir:
+            mod_name = 'not_main'
+            script_name = self._make_test_script(script_dir, mod_name)
+            msg = "can't find '__main__' module in %r" % script_dir
+            self._check_import_error(script_dir, msg)
+
+    def test_zipfile(self):
+        with temp_dir() as script_dir:
+            mod_name = '__main__'
+            script_name = self._make_test_script(script_dir, mod_name)
+            zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name)
+            self._check_script(zip_name, "<run_path>", fname, zip_name, '')
+
+    def test_zipfile_compiled(self):
+        with temp_dir() as script_dir:
+            mod_name = '__main__'
+            script_name = self._make_test_script(script_dir, mod_name)
+            compiled_name = compile_script(script_name)
+            zip_name, fname = make_zip_script(script_dir, 'test_zip', compiled_name)
+            self._check_script(zip_name, "<run_path>", fname, zip_name, '')
+
+    def test_zipfile_error(self):
+        with temp_dir() as script_dir:
+            mod_name = 'not_main'
+            script_name = self._make_test_script(script_dir, mod_name)
+            zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name)
+            msg = "can't find '__main__' module in %r" % zip_name
+            self._check_import_error(zip_name, msg)
+
+    def test_main_recursion_error(self):
+        with temp_dir() as script_dir, temp_dir() as dummy_dir:
+            mod_name = '__main__'
+            source = ("import runpy\n"
+                      "runpy.run_path(%r)\n") % dummy_dir
+            script_name = self._make_test_script(script_dir, mod_name, source)
+            zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name)
+            msg = "recursion depth exceeded"
+            self.assertRaisesRegexp(RuntimeError, msg, run_path, zip_name)
+
+
+
 def test_main():
-    run_unittest(RunModuleCodeTest)
-    run_unittest(RunModuleTest)
+    run_unittest(RunModuleCodeTest, RunModuleTest, RunPathTest)
 
 if __name__ == "__main__":
     test_main()
diff --git a/Lib/test/test_zipimport_support.py b/Lib/test/test_zipimport_support.py
index 94a65fc..3a99eb7 100644
--- a/Lib/test/test_zipimport_support.py
+++ b/Lib/test/test_zipimport_support.py
@@ -14,6 +14,9 @@
 import inspect
 import linecache
 import pdb
+from test.script_helper import (spawn_python, kill_python, run_python,
+                                temp_dir, make_script, compile_script,
+                                make_pkg, make_zip_script, make_zip_pkg)
 
 verbose = test.support.verbose
 
@@ -29,11 +32,6 @@
 # Retrieve some helpers from other test cases
 from test import test_doctest, sample_doctest
 from test.test_importhooks import ImportHooksBaseTestCase
-from test.test_cmd_line_script import temp_dir, _run_python,        \
-                                      _spawn_python, _kill_python,  \
-                                      _make_test_script,            \
-                                      _compile_test_script,         \
-                                      _make_test_zip, _make_test_pkg
 
 
 def _run_object_doctest(obj, module):
@@ -78,10 +76,10 @@
     def test_inspect_getsource_issue4223(self):
         test_src = "def foo(): pass\n"
         with temp_dir() as d:
-            init_name = _make_test_script(d, '__init__', test_src)
+            init_name = make_script(d, '__init__', test_src)
             name_in_zip = os.path.join('zip_pkg',
                                        os.path.basename(init_name))
-            zip_name, run_name = _make_test_zip(d, 'test_zip',
+            zip_name, run_name = make_zip_script(d, 'test_zip',
                                                 init_name, name_in_zip)
             os.remove(init_name)
             sys.path.insert(0, zip_name)
@@ -106,9 +104,9 @@
         sample_src = sample_src.replace("test.test_doctest",
                                         "test_zipped_doctest")
         with temp_dir() as d:
-            script_name = _make_test_script(d, 'test_zipped_doctest',
+            script_name = make_script(d, 'test_zipped_doctest',
                                             test_src)
-            zip_name, run_name = _make_test_zip(d, 'test_zip',
+            zip_name, run_name = make_zip_script(d, 'test_zip',
                                                 script_name)
             z = zipfile.ZipFile(zip_name, 'a')
             z.writestr("sample_zipped_doctest.py", sample_src)
@@ -180,23 +178,23 @@
                     """)
         pattern = 'File "%s", line 2, in %s'
         with temp_dir() as d:
-            script_name = _make_test_script(d, 'script', test_src)
-            exit_code, data = _run_python(script_name)
+            script_name = make_script(d, 'script', test_src)
+            exit_code, data = run_python(script_name)
             expected = pattern % (script_name, "__main__.Test")
             if verbose:
                 print ("Expected line", expected)
                 print ("Got stdout:")
                 print (data)
-            self.assertTrue(expected in data)
-            zip_name, run_name = _make_test_zip(d, "test_zip",
+            self.assertTrue(expected.encode('utf-8') in data)
+            zip_name, run_name = make_zip_script(d, "test_zip",
                                                 script_name, '__main__.py')
-            exit_code, data = _run_python(zip_name)
+            exit_code, data = run_python(zip_name)
             expected = pattern % (run_name, "__main__.Test")
             if verbose:
                 print ("Expected line", expected)
                 print ("Got stdout:")
                 print (data)
-            self.assertTrue(expected in data)
+            self.assertTrue(expected.encode('utf-8') in data)
 
     def test_pdb_issue4201(self):
         test_src = textwrap.dedent("""\
@@ -207,17 +205,17 @@
                     pdb.runcall(f)
                     """)
         with temp_dir() as d:
-            script_name = _make_test_script(d, 'script', test_src)
-            p = _spawn_python(script_name)
+            script_name = make_script(d, 'script', test_src)
+            p = spawn_python(script_name)
             p.stdin.write(b'l\n')
-            data = _kill_python(p).decode()
-            self.assertTrue(script_name in data)
-            zip_name, run_name = _make_test_zip(d, "test_zip",
+            data = kill_python(p)
+            self.assertTrue(script_name.encode('utf-8') in data)
+            zip_name, run_name = make_zip_script(d, "test_zip",
                                                 script_name, '__main__.py')
-            p = _spawn_python(zip_name)
+            p = spawn_python(zip_name)
             p.stdin.write(b'l\n')
-            data = _kill_python(p).decode()
-            self.assertTrue(run_name in data)
+            data = kill_python(p)
+            self.assertTrue(run_name.encode('utf-8') in data)
 
 
 def test_main():