Пример #1
0
 def test_custom_executable(self):
     fn = os.path.join(HERE, 'dummy-0.1-py27-none-any.whl')
     for executable in 'mypython', None:
         dstdir = tempfile.mkdtemp()
         self.addCleanup(shutil.rmtree, dstdir)
         w = Wheel(fn)
         paths = {'prefix': dstdir}
         for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
             paths[key] = os.path.join(dstdir, key)
         maker = ScriptMaker(None, None)
         maker.variants = set([''])
         maker.executable = executable
         w.install(paths, maker)
         # On Windows there will be an exe file, and on POSIX a text file.
         # The test is structured to not care.
         p = paths['scripts']
         # there should be just one file in the directory - dummy.py/dummy.exe
         p = os.path.join(p, os.listdir(p)[0])
         with open(p, 'rb') as f:
             data = f.read()
         if executable is None:
             expected = fsencode(get_executable())
         else:
             expected = executable.encode('utf-8')
         expected = b'#!' + expected + b' -E'
         self.assertIn(expected, data)
Пример #2
0
    def test_launcher_run_with_interpreter_args(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=True)

        # add '-O' option to shebang to run in optimized mode
        with open(os.path.join(HERE, 'scripts', 'script6.py'), 'r') as src:
            with open(os.path.join(srcdir, 'script6-optimized.py'),
                      'w') as dst:
                shebang = src.readline().rstrip()
                dst.write(shebang + " -O\n")
                dst.write(src.read())

        files = maker.make('script6-optimized.py')
        self.assertEqual(len(files), 1)
        p = subprocess.Popen([files[0], 'Test Argument'],
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate('input'.encode('ascii'))
        actual = stdout.decode('ascii').replace('\r\n', '\n')
        expected = textwrap.dedent("""
            script6-optimized.exe
            ['Test Argument']
            'input'
            """).lstrip()  # 'non-optimized' is not printed this time
        self.assertEqual(actual, expected)
Пример #3
0
 def test_custom_executable(self):
     fn = os.path.join(HERE, "dummy-0.1-py27-none-any.whl")
     for executable in "mypython", None:
         dstdir = tempfile.mkdtemp()
         self.addCleanup(shutil.rmtree, dstdir)
         w = Wheel(fn)
         paths = {"prefix": dstdir}
         for key in ("purelib", "platlib", "headers", "scripts", "data"):
             paths[key] = os.path.join(dstdir, key)
         maker = ScriptMaker(None, None)
         maker.variants = set([""])
         maker.executable = executable
         w.install(paths, maker)
         # On Windows there will be an exe file, and on POSIX a text file.
         # The test is structured to not care.
         p = paths["scripts"]
         # there should be just one file in the directory - dummy.py/dummy.exe
         p = os.path.join(p, os.listdir(p)[0])
         with open(p, "rb") as f:
             data = f.read()
         if executable is None:
             expected = fsencode(get_executable())
         else:
             expected = executable.encode("utf-8")
         expected = b"#!" + expected + b" -E"
         if not sysconfig.is_python_build():
             self.assertIn(expected, data)
Пример #4
0
    def test_launcher_run_with_interpreter_args(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=True)

        # add '-O' option to shebang to run in optimized mode
        with open(os.path.join(HERE, 'scripts', 'script6.py'), 'r') as src:
            with open(os.path.join(srcdir, 'script6-optimized.py'), 'w') as dst:
                shebang = src.readline().rstrip()
                dst.write(shebang + " -O\n")
                dst.write(src.read())

        files = maker.make('script6-optimized.py')
        self.assertEqual(len(files), 1)
        p = subprocess.Popen([files[0], 'Test Argument'],
                             stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate('input'.encode('ascii'))
        actual = stdout.decode('ascii').replace('\r\n', '\n')
        expected = textwrap.dedent("""
            script6-optimized.exe
            ['Test Argument']
            'input'
            """).lstrip()  # 'non-optimized' is not printed this time
        self.assertEqual(actual, expected)
Пример #5
0
 def test_custom_executable(self):
     fn = os.path.join(HERE, 'dummy-0.1-py27-none-any.whl')
     for executable in 'mypython', None:
         dstdir = tempfile.mkdtemp()
         self.addCleanup(shutil.rmtree, dstdir)
         w = Wheel(fn)
         paths = {'prefix': dstdir}
         for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
             paths[key] = os.path.join(dstdir, key)
         maker = ScriptMaker(None, None)
         maker.variants = set([''])
         maker.executable = executable
         w.install(paths, maker)
         # On Windows there will be an exe file, and on POSIX a text file.
         # The test is structured to not care.
         p = paths['scripts']
         # there should be just one file in the directory - dummy.py/dummy.exe
         p = os.path.join(p, os.listdir(p)[0])
         with open(p, 'rb') as f:
             data = f.read()
         if executable is None:
             expected = fsencode(get_executable())
         else:
             expected = executable.encode('utf-8')
         expected = b'#!' + expected + b' -E'
         if not sysconfig.is_python_build():
             self.assertIn(expected, data)
Пример #6
0
 def update_shebangs(self, new_path: str) -> None:
     """Update the shebang lines"""
     scripts = self.get_paths()["scripts"]
     maker = ScriptMaker(None, None)
     maker.executable = new_path
     shebang = maker._get_shebang("utf-8").rstrip()
     for child in Path(scripts).iterdir():
         if not child.is_file() or child.suffix not in (".exe", ".py", ""):
             continue
         child.write_bytes(
             re.sub(rb"#!.+?python.*?$", shebang, child.read_bytes(), flags=re.M)
         )
Пример #7
0
def fix_entry_points(*distribution_names):
    """Re-make all entry_points for the given distributions. This is a workaround for
    the fact that conda doesn't support GUI scripts. The filenames on disk remain the
    same, so conda can still uninstall cleanly - we're just fixing the files in-place"""

    # There are many places scripts can go, but we're in conda so the main scripts dir
    # at sysconfig.get_path('scripts') is the only possibility for us:
    maker = ScriptMaker(None, sysconfig.get_path('scripts'))
    maker.clobber = True  # Overwrite existing scripts
    maker.variants = {''}  # Don't make variants with Python major.minor suffixes

    for name in distribution_names:
        # Get the script specs and format them how Scriptmaker wants them
        distribution = importlib_metadata.Distribution.from_name(name)

        # I've seen enough bugs with entry_points in conda that I feel the need to
        # clobber its console_scripts too, even though they are officially supported:
        console_scripts = [
            f'{e.name} = {e.value}'
            for e in distribution.entry_points
            if e.group == 'console_scripts'
        ]
        gui_scripts = [
            f'{e.name} = {e.value}'
            for e in distribution.entry_points
            if e.group == 'gui_scripts'
        ]

        # Make 'em:
        maker.make_multiple(console_scripts)
        maker.make_multiple(gui_scripts, {'gui': True})
Пример #8
0
    def test_version_incompatibility(self):
        class Warner(object):
            def __call__(self, wheel_version, file_version):
                self.wheel_version = wheel_version
                self.file_version = file_version

        fn = os.path.join(HERE, 'dummy-0.1-py27-none-any.whl')
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        w = Wheel(fn)
        paths = {'prefix': dstdir}
        for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
            paths[key] = os.path.join(dstdir, key)
        warner = Warner()
        maker = ScriptMaker(None, None)
        w.install(paths, maker, warner=warner)
        self.assertEqual(warner.wheel_version, w.wheel_version)
        self.assertEqual(warner.file_version, (2, 0))
        # Now set the wheel's instance to the higher value and ensure
        # warner isn't called
        warner = Warner()
        w.wheel_version = (2, 0)
        w.install(paths, maker, warner=warner)
        self.assertFalse(hasattr(warner, 'wheel_version'))
        self.assertFalse(hasattr(warner, 'file_version'))
Пример #9
0
 def test_custom_shebang(self):
     # Construct an executable with a space in it
     self.maker.executable = 'an executable with spaces'
     filenames = self.maker.make('script1.py')
     with open(filenames[0], 'rb') as f:
         first_line = f.readline()
         second_line = f.readline()
         third_line = f.readline()
     self.assertEqual(first_line, b'#!/bin/sh\n')
     self.assertEqual(second_line, b"'''exec' an executable with "
                      b'spaces "$0" "$@"\n')
     self.assertEqual(third_line, b"' '''\n")
     # Python 3.3 cannot create a venv in an existing directory
     if venv and sys.version_info[:2] >= (3, 4):
         if sys.platform == 'darwin':
             # Supposedly 512, but various symlinks mean that temp folder
             # names get larger than you'd expect ... might vary on different
             # OS versions, too
             dlen = 220
         else:
             dlen = 127
         dstdir = tempfile.mkdtemp(suffix='cataaaaaa' + 'a' * dlen)
         self.addCleanup(shutil.rmtree, dstdir)
         bindir = os.path.join(dstdir, 'bin')
         maker = ScriptMaker(self.maker.source_dir,
                             bindir,
                             add_launchers=False)
         venv.create(dstdir)
         maker.executable = os.path.join(bindir, 'python')
         filenames = maker.make('script8.py')
         p = subprocess.Popen(filenames[0],
                              shell=True,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
         stdout, stderr = p.communicate()
         self.assertEqual(p.returncode, 0)
         self.assertEqual(stderr, b'')
         expected = os.path.realpath(maker.executable)  # symlinks on OS X
         actual = os.path.realpath(stdout.strip())
         self.assertEqual(actual, expected.encode('utf-8'))
Пример #10
0
 def test_shebangs_custom_executable(self):
     srcdir = tempfile.mkdtemp()
     self.addCleanup(shutil.rmtree, srcdir)
     dstdir = tempfile.mkdtemp()
     self.addCleanup(shutil.rmtree, dstdir)
     maker = ScriptMaker(srcdir, dstdir, add_launchers=False)
     maker.executable = 'this_should_appear_in_the_shebang_line'
     #let's create the script to be copied. It has a vanilla shebang line.
     fn = os.path.join(srcdir, 'copied')
     with open(fn, 'w') as f:
         f.write(COPIED_SCRIPT)
     # Let's ask the maker to copy the script, and see what the shebang is
     # in the copy.
     filenames = maker.make('copied')
     with open(filenames[0], 'r') as f:
         actual = f.readline()
     self.assertIn(maker.executable, actual)
     # Now let's make a script from a callable
     filenames = maker.make(MADE_SCRIPT)
     with open(filenames[0], 'r') as f:
         actual = f.readline()
     self.assertIn(maker.executable, actual)
Пример #11
0
    def do_build_and_install(self, dist):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)

        paths = install_dist(dist, srcdir)
        paths['prefix'] = srcdir
        w = Wheel()
        w.name = paths.pop('name')
        w.version = paths.pop('version')
        w.dirname = srcdir
        pathname = w.build(paths)
        self.assertTrue(os.path.exists(pathname))

        paths = {'prefix': dstdir}
        for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
            paths[key] = os.path.join(dstdir, key)
        w = Wheel(pathname)
        maker = ScriptMaker(None, None, add_launchers=False)
        maker.executable = os.path.join(paths['scripts'], 'python')
        dist = w.install(paths, maker)
        self.assertIsNotNone(dist)
        self.assertEqual(dist.name, w.name)
        self.assertEqual(dist.version, w.version)
        shared = dist.shared_locations
        self.assertTrue(shared)
        os.remove(pathname)
        sm = Manifest(srcdir)
        sm.findall()
        sfiles = set([os.path.relpath(p, srcdir) for p in sm.allfiles])
        dm = Manifest(dstdir)
        dm.findall()
        dfiles = set([os.path.relpath(p, dstdir) for p in dm.allfiles])
        omitted = sfiles - dfiles
        omitted = omitted.pop()
        endings = os.path.join('.dist-info', 'WHEEL'), '.pyc', '.pyo'
        self.assertTrue(omitted.endswith(endings))
Пример #12
0
    def do_build_and_install(self, dist):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)

        paths = install_dist(dist, srcdir)
        paths['prefix'] = srcdir
        w = Wheel()
        w.name = paths.pop('name')
        w.version = paths.pop('version')
        w.dirname = srcdir
        pathname = w.build(paths)
        self.assertTrue(os.path.exists(pathname))

        paths = {'prefix': dstdir}
        for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
            paths[key] = os.path.join(dstdir, key)
        w = Wheel(pathname)
        maker = ScriptMaker(None, None, add_launchers=False)
        maker.executable = os.path.join(paths['scripts'], 'python')
        dist = w.install(paths, maker)
        self.assertIsNotNone(dist)
        self.assertEqual(dist.name, w.name)
        self.assertEqual(dist.version, w.version)
        shared = dist.shared_locations
        self.assertTrue(shared)
        os.remove(pathname)
        sm = Manifest(srcdir)
        sm.findall()
        sfiles = set([os.path.relpath(p, srcdir) for p in sm.allfiles])
        dm = Manifest(dstdir)
        dm.findall()
        dfiles = set([os.path.relpath(p, dstdir) for p in dm.allfiles])
        omitted = sfiles - dfiles
        omitted = omitted.pop()
        endings = os.path.join('.dist-info', 'WHEEL'), '.pyc', '.pyo'
        self.assertTrue(omitted.endswith(endings))
Пример #13
0
    def do_build_and_install(self, dist):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)

        paths = install_dist(dist, srcdir)
        paths["prefix"] = srcdir
        w = Wheel()
        w.name = paths.pop("name")
        w.version = paths.pop("version")
        w.dirname = srcdir
        pathname = w.build(paths)
        self.assertTrue(os.path.exists(pathname))

        paths = {"prefix": dstdir}
        for key in ("purelib", "platlib", "headers", "scripts", "data"):
            paths[key] = os.path.join(dstdir, key)
        w = Wheel(pathname)
        maker = ScriptMaker(None, None, add_launchers=False)
        maker.executable = os.path.join(paths["scripts"], "python")
        dist = w.install(paths, maker)
        self.assertIsNotNone(dist)
        self.assertEqual(dist.name, w.name)
        self.assertEqual(dist.version, w.version)
        shared = dist.shared_locations
        self.assertTrue(shared)
        os.remove(pathname)
        sm = Manifest(srcdir)
        sm.findall()
        sfiles = set([os.path.relpath(p, srcdir) for p in sm.allfiles])
        dm = Manifest(dstdir)
        dm.findall()
        dfiles = set([os.path.relpath(p, dstdir) for p in dm.allfiles])
        omitted = sfiles - dfiles
        omitted = omitted.pop()
        endings = os.path.join(".dist-info", "WHEEL"), ".pyc", ".pyo"
        self.assertTrue(omitted.endswith(endings))
Пример #14
0
 def test_shebangs_custom_executable(self):
     srcdir = tempfile.mkdtemp()
     self.addCleanup(shutil.rmtree, srcdir)
     dstdir = tempfile.mkdtemp()
     self.addCleanup(shutil.rmtree, dstdir)
     maker = ScriptMaker(srcdir, dstdir, add_launchers=False)
     maker.executable = 'this_should_appear_in_the_shebang_line(中文)'
     # let's create the script to be copied. It has a vanilla shebang line,
     # with some Unicode in it.
     fn = os.path.join(srcdir, 'copied')
     with open(fn, 'w') as f:
         f.write(COPIED_SCRIPT)
     # Let's ask the maker to copy the script, and see what the shebang is
     # in the copy.
     filenames = maker.make('copied')
     with open(filenames[0], 'rb') as f:
         actual = f.readline().decode('utf-8')
     self.assertIn(maker.executable, actual)
     # Now let's make a script from a callable
     filenames = maker.make(MADE_SCRIPT)
     with open(filenames[0], 'rb') as f:
         actual = f.readline().decode('utf-8')
     self.assertIn(maker.executable, actual)
Пример #15
0
 def _create_console_entry_point(self, name, value, to_folder, version_info):
     result = []
     maker = ScriptMaker(None, str(to_folder))
     maker.clobber = True  # overwrite
     maker.variants = {""}  # set within patch_distlib_correct_variants
     maker.set_mode = True  # ensure they are executable
     # calling private until https://bitbucket.org/pypa/distlib/issues/135/expose-_enquote_executable-as-public
     maker.executable = _enquote_executable(str(self._creator.exe))
     specification = "{} = {}".format(name, value)
     with self.patch_distlib_correct_variants(version_info, maker):
         new_files = maker.make(specification)
     result.extend(Path(i) for i in new_files)
     return result
Пример #16
0
    def _create_console_entry_point(self, name, value, to_folder):
        result = []
        from distlib.scripts import ScriptMaker

        maker = ScriptMaker(None, str(to_folder))
        maker.clobber = True  # overwrite
        maker.variants = {"", "X", "X.Y"}  # create all variants
        maker.set_mode = True  # ensure they are executable
        maker.executable = str(self._creator.exe)
        specification = "{} = {}".format(name, value)
        new_files = maker.make(specification)
        result.extend(Path(i) for i in new_files)
        return result
 def __install_wheel(
     self,
     filepath: str,
     force: bool,
     upgrade: bool,
     options: dict,
 ) -> InstalledDistribution:
     '''Install wheel to selected paths.'''
     wheel = Wheel(filepath)
     try:
         wheel.verify()
         return wheel.install(
             paths=self.distribution_path.paths,
             maker=ScriptMaker(None, None),
             # bytecode_hashed_invalidation=True
         )
     except DistlibException:
         print('wheel did not pass validation')
Пример #18
0
    def _create_console_entry_point(self, name, value, to_folder):
        result = []
        if IS_WIN:
            # windows doesn't support simple script files, so fallback to more complicated exe generator
            from distlib.scripts import ScriptMaker

            maker = ScriptMaker(None, str(to_folder))
            maker.clobber = True  # overwrite
            maker.variants = {"", "X", "X.Y"}  # create all variants
            maker.set_mode = True  # ensure they are executable
            maker.executable = str(self._creator.exe)
            specification = "{} = {}".format(name, value)
            new_files = maker.make(specification)
            result.extend(Path(i) for i in new_files)
        else:
            module, func = value.split(":")
            content = (
                dedent(
                    """
            #!{0}
            # -*- coding: utf-8 -*-
            import re
            import sys

            from {1} import {2}

            if __name__ == "__main__":
                sys.argv[0] = re.sub(r"(-script.pyw?|.exe)?$", "", sys.argv[0])
                sys.exit({2}())
            """
                )
                .lstrip()
                .format(self._creator.exe, module, func)
            )

            version = self._creator.interpreter.version_info
            for new_name in (
                name,
                "{}{}".format(name, version.major),
                "{}-{}.{}".format(name, version.major, version.minor),
            ):
                exe = to_folder / new_name
                exe.write_text(content, encoding="utf-8")
                make_exe(exe)
                result.append(exe)
        return result
Пример #19
0
    def install_wheels(wheels_folder, install_folder):
        logging.getLogger('distlib').setLevel(logging.ERROR)
        if not os.path.exists(install_folder):
            os.makedirs(install_folder)

        from distlib.wheel import Wheel
        from distlib.scripts import ScriptMaker

        paths = {
            'prefix': '',
            'purelib': install_folder,
            'platlib': install_folder,
            'scripts': '',
            'headers': '',
            'data': ''}
        files = os.listdir(wheels_folder)
        for f in [os.path.join(wheels_folder, f) for f in files]:
            wheel = Wheel(f)
            wheel.install(paths, ScriptMaker(None, None), lib_only=True)
Пример #20
0
    def create_production_scripts(self, tool, venv_session):
        """Create Rez production used binary scripts

        The binary script will be executed with Python interpreter flag -E,
        which will ignore all PYTHON* env vars, e.g. PYTHONPATH and PYTHONHOME.

        """
        _log.info("Generating production scripts..")

        site_packages = venv_session.creator.purelib
        bin_path = venv_session.creator.bin_dir

        if tool.edit:
            egg_link = site_packages / ("%s.egg-link" % tool.name)
            if not egg_link.is_file():
                _log.error("Tool %r installed in edit mode, but unable "
                           "to find egg-link for generating production "
                           "scripts from source. File not exists: %s" %
                           (tool.name, egg_link))
                return

            with open(str(egg_link), "r") as f:
                package_location = f.readline().strip()
            path = [str(package_location)]
        else:
            path = [str(site_packages)]

        dists = Distribution.discover(name=tool.name, path=path)
        specifications = {
            ep.name: "{ep.name} = {ep.value}".format(ep=ep)
            for dist in dists for ep in dist.entry_points
            if ep.group == "console_scripts"
        }

        # delete bin files written into virtualenv
        # this also avoided naming conflict between script 'rez' and dir 'rez'
        for script_name in specifications.keys():
            script_path = bin_path / script_name
            if script_path.is_file():
                os.remove(str(script_path))

        venv_name = tool.name if tool.isolation else "rez"
        prod_bin_path = self._revision.production_bin_dir(venv_name)
        makedirs(prod_bin_path)

        maker = ScriptMaker(source_dir=None, target_dir=str(prod_bin_path))
        maker.executable = str(venv_session.creator.exe)

        # Align with wheel
        #
        # Ensure we don't generate any variants for scripts because this is
        # almost never what somebody wants.
        # See https://bitbucket.org/pypa/distlib/issue/35/
        maker.variants = {""}
        # Ensure old scripts are overwritten.
        # See https://github.com/pypa/pip/issues/1800
        maker.clobber = True
        # This is required because otherwise distlib creates scripts that are
        # not executable.
        # See https://bitbucket.org/pypa/distlib/issue/32/
        maker.set_mode = True

        if self._rez_in_edit:
            # Allow pre-caching rez_bin_path on script entry if environ var
            # `REZUP_EDIT_IN_PRODUCTION` is set with non-empty value.
            # See https://github.com/davidlatwe/rezup/pull/56
            maker.script_template = r'''# -*- coding: utf-8 -*-
import re
import os
import sys
from %(module)s import %(import_name)s
if os.getenv("REZUP_EDIT_IN_PRODUCTION"):
    from rez.system import system
    setattr(system, 'rez_bin_path', r'{rez_bin_path}')
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(%(func)s())
'''.format(rez_bin_path=str(prod_bin_path))

        scripts = maker.make_multiple(
            specifications=specifications.values(),
            options=dict(interpreter_args=list(tool.flags)))

        return scripts
Пример #21
0
def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
                     pycompile=True, scheme=None):
    """Install a wheel"""

    if not scheme:
        scheme = distutils_scheme(name, user=user, home=home, root=root)

    if root_is_purelib(name, wheeldir):
        lib_dir = scheme['purelib']
    else:
        lib_dir = scheme['platlib']

    info_dir = []
    data_dirs = []
    source = wheeldir.rstrip(os.path.sep) + os.path.sep

    # Record details of the files moved
    #   installed = files copied from the wheel to the destination
    #   changed = files changed while installing (scripts #! line typically)
    #   generated = files newly generated during the install (script wrappers)
    installed = {}
    changed = set()
    generated = []

    # Compile all of the pyc files that we're going to be installing
    if pycompile:
        compileall.compile_dir(source, force=True, quiet=True)

    def normpath(src, p):
        return make_path_relative(src, p).replace(os.path.sep, '/')

    def record_installed(srcfile, destfile, modified=False):
        """Map archive RECORD paths to installation RECORD paths."""
        oldpath = normpath(srcfile, wheeldir)
        newpath = normpath(destfile, lib_dir)
        installed[oldpath] = newpath
        if modified:
            changed.add(destfile)

    def clobber(source, dest, is_base, fixer=None, filter=None):
        if not os.path.exists(dest): # common for the 'include' path
            os.makedirs(dest)

        for dir, subdirs, files in os.walk(source):
            basedir = dir[len(source):].lstrip(os.path.sep)
            destdir = os.path.join(dest, basedir)
            if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'):
                continue
            for s in subdirs:
                destsubdir = os.path.join(dest, basedir, s)
                if is_base and basedir == '' and destsubdir.endswith('.data'):
                    data_dirs.append(s)
                    continue
                elif (is_base
                    and s.endswith('.dist-info')
                    # is self.req.project_name case preserving?
                    and s.lower().startswith(req.project_name.replace('-', '_').lower())):
                    assert not info_dir, 'Multiple .dist-info directories'
                    info_dir.append(destsubdir)
            for f in files:
                # Skip unwanted files
                if filter and filter(f):
                    continue
                srcfile = os.path.join(dir, f)
                destfile = os.path.join(dest, basedir, f)
                # directory creation is lazy and after the file filtering above
                # to ensure we don't install empty dirs; empty dirs can't be
                # uninstalled.
                if not os.path.exists(destdir):
                    os.makedirs(destdir)
                # use copy2 (not move) to be extra sure we're not moving
                # directories over; copy2 fails for directories.  this would
                # fail tests (not during released/user execution)
                shutil.copy2(srcfile, destfile)
                changed = False
                if fixer:
                    changed = fixer(destfile)
                record_installed(srcfile, destfile, changed)

    clobber(source, lib_dir, True)

    assert info_dir, "%s .dist-info directory not found" % req

    # Get the defined entry points
    ep_file = os.path.join(info_dir[0], 'entry_points.txt')
    console, gui = get_entrypoints(ep_file)

    def is_entrypoint_wrapper(name):
        # EP, EP.exe and EP-script.py are scripts generated for
        # entry point EP by setuptools
        if name.lower().endswith('.exe'):
            matchname = name[:-4]
        elif name.lower().endswith('-script.py'):
            matchname = name[:-10]
        elif name.lower().endswith(".pya"):
            matchname = name[:-4]
        else:
            matchname = name
        # Ignore setuptools-generated scripts
        return (matchname in console or matchname in gui)

    for datadir in data_dirs:
        fixer = None
        filter = None
        for subdir in os.listdir(os.path.join(wheeldir, datadir)):
            fixer = None
            if subdir == 'scripts':
                fixer = fix_script
                filter = is_entrypoint_wrapper
            source = os.path.join(wheeldir, datadir, subdir)
            dest = scheme[subdir]
            clobber(source, dest, False, fixer=fixer, filter=filter)

    maker = ScriptMaker(None, scheme['scripts'])

    # Ensure we don't generate any variants for scripts because this is almost
    # never what somebody wants.
    # See https://bitbucket.org/pypa/distlib/issue/35/
    maker.variants = set(('', ))

    # This is required because otherwise distlib creates scripts that are not
    # executable.
    # See https://bitbucket.org/pypa/distlib/issue/32/
    maker.set_mode = True

    # Simplify the script and fix the fact that the default script swallows
    # every single stack trace.
    # See https://bitbucket.org/pypa/distlib/issue/34/
    # See https://bitbucket.org/pypa/distlib/issue/33/
    def _get_script_text(entry):
        return maker.script_template % {
            "module": entry.prefix,
            "import_name": entry.suffix.split(".")[0],
            "func": entry.suffix,
        }

    maker._get_script_text = _get_script_text
    maker.script_template = """# -*- coding: utf-8 -*-
import re
import sys

from %(module)s import %(import_name)s

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(%(func)s())
"""

    # Special case pip and setuptools to generate versioned wrappers
    #
    # The issue is that some projects (specifically, pip and setuptools) use
    # code in setup.py to create "versioned" entry points - pip2.7 on Python
    # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into
    # the wheel metadata at build time, and so if the wheel is installed with
    # a *different* version of Python the entry points will be wrong. The
    # correct fix for this is to enhance the metadata to be able to describe
    # such versioned entry points, but that won't happen till Metadata 2.0 is
    # available.
    # In the meantime, projects using versioned entry points will either have
    # incorrect versioned entry points, or they will not be able to distribute
    # "universal" wheels (i.e., they will need a wheel per Python version).
    #
    # Because setuptools and pip are bundled with _ensurepip and virtualenv,
    # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we
    # override the versioned entry points in the wheel and generate the
    # correct ones. This code is purely a short-term measure until Metadat 2.0
    # is available.
    #
    # To add the level of hack in this section of code, in order to support
    # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment
    # variable which will control which version scripts get installed.
    #
    # ENSUREPIP_OPTIONS=altinstall
    #   - Only pipX.Y and easy_install-X.Y will be generated and installed
    # ENSUREPIP_OPTIONS=install
    #   - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note
    #     that this option is technically if ENSUREPIP_OPTIONS is set and is
    #     not altinstall
    # DEFAULT
    #   - The default behavior is to install pip, pipX, pipX.Y, easy_install
    #     and easy_install-X.Y.
    pip_script = console.pop('pip', None)
    if pip_script:
        if "ENSUREPIP_OPTIONS" not in os.environ:
            spec = 'pip = ' + pip_script
            generated.extend(maker.make(spec))

        if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall":
            spec = 'pip%s = %s' % (sys.version[:1], pip_script)
            generated.extend(maker.make(spec))

        spec = 'pip%s = %s' % (sys.version[:3], pip_script)
        generated.extend(maker.make(spec))
        # Delete any other versioned pip entry points
        pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)]
        for k in pip_ep:
            del console[k]
    easy_install_script = console.pop('easy_install', None)
    if easy_install_script:
        if "ENSUREPIP_OPTIONS" not in os.environ:
            spec = 'easy_install = ' + easy_install_script
            generated.extend(maker.make(spec))

        spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script)
        generated.extend(maker.make(spec))
        # Delete any other versioned easy_install entry points
        easy_install_ep = [k for k in console
                if re.match(r'easy_install(-\d\.\d)?$', k)]
        for k in easy_install_ep:
            del console[k]

    # Generate the console and GUI entry points specified in the wheel
    if len(console) > 0:
        generated.extend(maker.make_multiple(['%s = %s' % kv for kv in console.items()]))
    if len(gui) > 0:
        generated.extend(maker.make_multiple(['%s = %s' % kv for kv in gui.items()], {'gui': True}))

    record = os.path.join(info_dir[0], 'RECORD')
    temp_record = os.path.join(info_dir[0], 'RECORD.pip')
    with open_for_csv(record, 'r') as record_in:
        with open_for_csv(temp_record, 'w+') as record_out:
            reader = csv.reader(record_in)
            writer = csv.writer(record_out)
            for row in reader:
                row[0] = installed.pop(row[0], row[0])
                if row[0] in changed:
                    row[1], row[2] = rehash(row[0])
                writer.writerow(row)
            for f in generated:
                h, l = rehash(f)
                writer.writerow((f, h, l))
            for f in installed:
                writer.writerow((installed[f], '', ''))
    shutil.move(temp_record, record)
Пример #22
0
 def setUp(self):
     source_dir = os.path.join(HERE, 'scripts')
     target_dir = tempfile.mkdtemp()
     self.maker = ScriptMaker(source_dir, target_dir, add_launchers=False)
Пример #23
0
class ScriptTestCase(unittest.TestCase):

    def setUp(self):
        source_dir = os.path.join(HERE, 'scripts')
        target_dir = tempfile.mkdtemp()
        self.maker = ScriptMaker(source_dir, target_dir, add_launchers=False)

    def tearDown(self):
        shutil.rmtree(self.maker.target_dir)

    @unittest.skipIf(sysconfig.is_python_build(), 'Test not appropriate for '
                     'Python source builds')
    def test_shebangs(self):
        executable = fsencode(get_executable())
        for fn in ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                   'shell.sh'):
            files = self.maker.make(fn)
            self.assertEqual(len(files), 1)
            d, f = os.path.split(files[0])
            self.assertEqual(f, fn)
            self.assertEqual(d, self.maker.target_dir)
            if fn.endswith('.py') and fn != 'foo.py':   # no shebang in foo.py
                with open(files[0], 'rb') as f:
                    first_line = f.readline()
                self.assertIn(executable, first_line)

    def test_shebangs_custom_executable(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=False)
        maker.executable = 'this_should_appear_in_the_shebang_line'
        #let's create the script to be copied. It has a vanilla shebang line.
        fn = os.path.join(srcdir, 'copied')
        with open(fn, 'w') as f:
            f.write(COPIED_SCRIPT)
        # Let's ask the maker to copy the script, and see what the shebang is
        # in the copy.
        filenames = maker.make('copied')
        with open(filenames[0], 'r') as f:
            actual = f.readline()
        self.assertIn(maker.executable, actual)
        # Now let's make a script from a callable
        filenames = maker.make(MADE_SCRIPT)
        with open(filenames[0], 'r') as f:
            actual = f.readline()
        self.assertIn(maker.executable, actual)

    def test_multiple(self):
        specs = ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                 'shell.sh')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        expected = set(specs)
        self.assertEqual(expected, set([os.path.basename(f) for f in files]))
        ofiles = os.listdir(self.maker.target_dir)
        self.assertEqual(expected, set(ofiles))

    def test_generation(self):
        self.maker.clobber = True
        for name in ('main', 'other_main'):
            for options in (None, {}, {'gui': False}, {'gui': True}):
                gui = options and options.get('gui', False)
                spec = 'foo = foo:' + name
                files = self.maker.make(spec, options)
                self.assertEqual(len(files), 2)
                actual = set()
                for f in files:
                    d, f = os.path.split(f)
                    actual.add(f)
                if os.name == 'nt':
                    if gui:
                        ext = 'pyw'
                    else:
                        ext = 'py'
                    expected = set(['foo.%s' % ext,
                                    'foo-%s.%s' % (sys.version[:3], ext)])
                else:
                    expected = set(['foo', 'foo-%s' % sys.version[:3]])
                self.assertEqual(actual, expected)
                self.assertEqual(d, self.maker.target_dir)
                for fn in files:
                    with open(fn, 'r') as f:
                        text = f.read()
                    self.assertIn("_resolve('foo', '%s')" % name, text)
                    if options and options['gui'] and os.name == 'nt':
                        first_line, rest = text.split('\n', 1)
                        self.assertIn('pythonw', first_line)

    def test_clobber(self):
        files = self.maker.make('foo = foo:main')
        saved_files = files
        self.assertGreaterEqual(len(files), 2)  # foo, foo-X.Y
        files = self.maker.make('foo = foo:main')
        self.assertFalse(files)
        self.maker.clobber = True
        files = self.maker.make('foo = foo:main')
        self.assertEqual(files, saved_files)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launchers(self):
        tlauncher = self.maker._get_launcher('t')
        self.maker.add_launchers = True
        specs = ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                 'shell.sh')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(('foo.py', 'script1.exe',
                                         'script2.exe', 'script3.exe',
                                         'shell.sh')))
        for fn in files:
            if not fn.endswith('.exe'):
                continue
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(tlauncher))

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_windows(self):
        wlauncher = self.maker._get_launcher('w')
        tlauncher = self.maker._get_launcher('t')
        self.maker.add_launchers = True
        executable = sys.executable.encode('utf-8')
        wexecutable = executable.replace(b'python.exe', b'pythonw.exe')
        files = self.maker.make('script4.py')
        self.assertEqual(len(files), 1)
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(['script4.exe']))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(wlauncher))
            self.assertIn(executable, data)
        # Now test making scripts gui and console
        files = self.maker.make('foo = foo:main', {'gui': True})
        self.assertEqual(len(files), 2)
        filenames = set([os.path.basename(f) for f in files])
        specific = sys.version[:3]
        self.assertEqual(filenames, set(('foo.exe', 'foo-%s.exe' % specific)))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(wlauncher))
            self.assertIn(wexecutable, data)

        files = self.maker.make('foo = foo:main')
        self.assertEqual(len(files), 2)
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(('foo.exe', 'foo-%s.exe' % specific)))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(tlauncher))
            self.assertIn(executable, data)

    def test_dry_run(self):
        self.maker.dry_run = True
        self.maker.variants = set([''])
        specs = ('foo.py', 'bar = foo:main')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        if os.name == 'nt':
            bar = 'bar.py'
        else:
            bar = 'bar'
        self.assertEqual(set(('foo.py', bar)),
                         set([os.path.basename(f) for f in files]))
        ofiles = os.listdir(self.maker.target_dir)
        self.assertFalse(ofiles)

    def test_script_run(self):
        files = self.maker.make('test = cgi:print_directory')
        self.assertEqual(len(files), 2)
        p = subprocess.Popen([sys.executable, files[0]],
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = p.communicate()
        self.assertIn(b'<H3>Current Working Directory:</H3>', stdout)
        self.assertIn(os.getcwd().encode('utf-8'), stdout)

    @unittest.skipUnless(os.name == 'posix', 'Test only valid for POSIX')
    def test_mode(self):
        files = self.maker.make('foo = foo:main')
        self.assertEqual(len(files), 2)
        for f in files:
            self.assertIn(os.stat(f).st_mode & 0o7777, (0o644, 0o664))
        self.maker.set_mode = True
        files = self.maker.make('bar = bar:main')
        self.assertEqual(len(files), 2)
        for f in files:
            self.assertIn(os.stat(f).st_mode & 0o7777, (0o755, 0o775))
Пример #24
0
 def setUp(self):
     source_dir = os.path.join(HERE, 'scripts')
     target_dir = tempfile.mkdtemp()
     self.maker = ScriptMaker(source_dir, target_dir, add_launchers=False)
Пример #25
0
class ScriptTestCase(unittest.TestCase):
    def setUp(self):
        source_dir = os.path.join(HERE, 'scripts')
        target_dir = tempfile.mkdtemp()
        self.maker = ScriptMaker(source_dir, target_dir, add_launchers=False)

    def tearDown(self):
        shutil.rmtree(self.maker.target_dir)

    @unittest.skipIf(sysconfig.is_python_build(), 'Test not appropriate for '
                     'Python source builds')
    def test_shebangs(self):
        executable = fsencode(get_executable())
        for fn in ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                   'shell.sh'):
            files = self.maker.make(fn)
            self.assertEqual(len(files), 1)
            d, f = os.path.split(files[0])
            self.assertEqual(f, fn)
            self.assertEqual(d, self.maker.target_dir)
            if fn.endswith('.py') and fn != 'foo.py':  # no shebang in foo.py
                with open(files[0], 'rb') as f:
                    first_line = f.readline()
                self.assertIn(executable, first_line)

    def test_shebangs_custom_executable(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=False)
        maker.executable = 'this_should_appear_in_the_shebang_line(中文)'
        # let's create the script to be copied. It has a vanilla shebang line,
        # with some Unicode in it.
        fn = os.path.join(srcdir, 'copied')
        with open(fn, 'w') as f:
            f.write(COPIED_SCRIPT)
        # Let's ask the maker to copy the script, and see what the shebang is
        # in the copy.
        filenames = maker.make('copied')
        with open(filenames[0], 'rb') as f:
            actual = f.readline().decode('utf-8')
        self.assertIn(maker.executable, actual)
        # Now let's make a script from a callable
        filenames = maker.make(MADE_SCRIPT)
        with open(filenames[0], 'rb') as f:
            actual = f.readline().decode('utf-8')
        self.assertIn(maker.executable, actual)

    def test_multiple(self):
        specs = ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                 'shell.sh', 'uwsgi_part')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        expected = set(specs)
        self.assertEqual(expected, set([os.path.basename(f) for f in files]))
        ofiles = os.listdir(self.maker.target_dir)
        self.assertEqual(expected, set(ofiles))

    def test_generation(self):
        self.maker.clobber = True
        for name in ('main', 'other_main'):
            for options in (None, {}, {'gui': False}, {'gui': True}):
                gui = options and options.get('gui', False)
                spec = 'foo = foo:' + name
                files = self.maker.make(spec, options)
                self.assertEqual(len(files), 2)
                actual = set()
                for f in files:
                    d, f = os.path.split(f)
                    actual.add(f)
                if os.name == 'nt':  # pragma: no cover
                    if gui:
                        ext = 'pyw'
                    else:
                        ext = 'py'
                    expected = set(
                        ['foo.%s' % ext,
                         'foo-%s.%s' % (sys.version[:3], ext)])
                else:
                    expected = set(['foo', 'foo-%s' % sys.version[:3]])
                self.assertEqual(actual, expected)
                self.assertEqual(d, self.maker.target_dir)
                for fn in files:
                    with open(fn, 'r') as f:
                        text = f.read()
                    self.assertIn("_resolve('foo', '%s')" % name, text)
                    if options and options[
                            'gui'] and os.name == 'nt':  # pragma: no cover
                        first_line, rest = text.split('\n', 1)
                        self.assertIn('pythonw', first_line)

    def test_clobber(self):
        files = self.maker.make('foo = foo:main')
        saved_files = files
        self.assertGreaterEqual(len(files), 2)  # foo, foo-X.Y
        files = self.maker.make('foo = foo:main')
        self.assertFalse(files)
        self.maker.clobber = True
        files = self.maker.make('foo = foo:main')
        self.assertEqual(files, saved_files)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launchers(self):  # pragma: no cover
        tlauncher = self.maker._get_launcher('t')
        self.maker.add_launchers = True
        specs = ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                 'shell.sh')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(
            filenames,
            set(('foo.py', 'script1.exe', 'script2.exe', 'script3.exe',
                 'shell.sh')))
        for fn in files:
            if not fn.endswith('.exe'):
                continue
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(tlauncher))

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launcher_run(self):
        self.maker.add_launchers = True
        files = self.maker.make('script6.py')
        self.assertEqual(len(files), 1)
        p = subprocess.Popen([files[0], 'Test Argument'],
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate('input'.encode('ascii'))
        actual = stdout.decode('ascii').replace('\r\n', '\n')
        expected = textwrap.dedent("""
            script6.exe
            ['Test Argument']
            'input'
            non-optimized
            """).lstrip()
        self.assertEqual(actual, expected)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launcher_run_with_interpreter_args(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=True)

        # add '-O' option to shebang to run in optimized mode
        with open(os.path.join(HERE, 'scripts', 'script6.py'), 'r') as src:
            with open(os.path.join(srcdir, 'script6-optimized.py'),
                      'w') as dst:
                shebang = src.readline().rstrip()
                dst.write(shebang + " -O\n")
                dst.write(src.read())

        files = maker.make('script6-optimized.py')
        self.assertEqual(len(files), 1)
        p = subprocess.Popen([files[0], 'Test Argument'],
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate('input'.encode('ascii'))
        actual = stdout.decode('ascii').replace('\r\n', '\n')
        expected = textwrap.dedent("""
            script6-optimized.exe
            ['Test Argument']
            'input'
            """).lstrip()  # 'non-optimized' is not printed this time
        self.assertEqual(actual, expected)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_windows(self):  # pragma: no cover
        wlauncher = self.maker._get_launcher('w')
        tlauncher = self.maker._get_launcher('t')
        self.maker.add_launchers = True
        executable = os.path.normcase(sys.executable).encode('utf-8')
        wexecutable = executable.replace(b'python.exe', b'pythonw.exe')
        files = self.maker.make('script4.py')
        self.assertEqual(len(files), 1)
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(['script4.exe']))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(wlauncher))
            self.assertIn(executable, data)
        # Now test making scripts gui and console
        files = self.maker.make('foo = foo:main', {'gui': True})
        self.assertEqual(len(files), 2)
        filenames = set([os.path.basename(f) for f in files])
        specific = sys.version[:3]
        self.assertEqual(filenames, set(('foo.exe', 'foo-%s.exe' % specific)))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(wlauncher))
            self.assertIn(wexecutable, data)

        files = self.maker.make('foo = foo:main')
        self.assertEqual(len(files), 2)
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(('foo.exe', 'foo-%s.exe' % specific)))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(tlauncher))
            self.assertIn(executable, data)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_windows_run(self):
        self.maker.add_launchers = True
        files = self.maker.make('script7.pyw')
        self.assertEqual(len(files), 1)

        test_output = os.path.join(self.maker.target_dir, 'test_output.txt')
        p = subprocess.Popen([files[0], test_output, 'Test Argument'],
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate()
        self.assertFalse(stdout)
        self.assertFalse(stderr)
        with open(test_output, 'rb') as f:
            actual = f.read().decode('ascii')
        self.assertEqual(actual, 'Test Argument')

    def test_dry_run(self):
        self.maker.dry_run = True
        self.maker.variants = set([''])
        specs = ('foo.py', 'bar = foo:main')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        if os.name == 'nt':  # pragma: no cover
            bar = 'bar.py'
        else:
            bar = 'bar'
        self.assertEqual(set(('foo.py', bar)),
                         set([os.path.basename(f) for f in files]))
        ofiles = os.listdir(self.maker.target_dir)
        self.assertFalse(ofiles)

    def test_script_run(self):
        files = self.maker.make('test = cgi:print_directory')
        self.assertEqual(len(files), 2)
        p = subprocess.Popen([sys.executable, files[0]],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        stdout, stderr = p.communicate()
        self.assertIn(b'<H3>Current Working Directory:</H3>', stdout)
        self.assertIn(os.getcwd().encode('utf-8'), stdout)

    @unittest.skipUnless(os.name == 'posix', 'Test only valid for POSIX')
    def test_mode(self):
        self.maker.set_mode = False
        files = self.maker.make('foo = foo:main')
        self.assertEqual(len(files), 2)
        for f in files:
            self.assertIn(os.stat(f).st_mode & 0o7777, (0o644, 0o664))
        self.maker.set_mode = True
        files = self.maker.make('bar = bar:main')
        self.assertEqual(len(files), 2)
        for f in files:
            self.assertIn(os.stat(f).st_mode & 0o7777, (0o755, 0o775))

    def test_interpreter_args(self):
        executable = fsencode(get_executable())
        options = {'interpreter_args': ['-E', '"foo bar"', 'baz frobozz']}
        self.maker.variants = set([''])
        files = self.maker.make('foo = bar:baz', options=options)
        self.assertEqual(len(files), 1)
        with open(files[0], 'rb') as f:
            shebang_line = f.readline()
        self.assertIn(executable, shebang_line)
        self.assertIn(b' -E "foo bar" baz frobozz', shebang_line)

    def test_args_on_copy(self):
        self.maker.variants = set([''])
        self.maker.executable = 'mypython'
        files = self.maker.make('script5.py')
        with open(files[0]) as f:
            actual = f.readline().strip()
        self.assertEqual(actual, '#!mypython -mzippy.activate')
        self.maker.executable = None
        os.remove(files[0])
        files = self.maker.make('script5.py')
        with open(files[0]) as f:
            actual = f.readline().strip()
        expected = '#!%s -mzippy.activate' % get_executable()
        self.assertEqual(actual, expected)

    def test_enquote_executable(self):
        for executable, expected in (('/no/spaces', '/no/spaces'),
                                     ('/i have/space', '"/i have/space"'),
                                     ('"/space prequoted"',
                                      '"/space prequoted"'),
                                     ('/usr/bin/env nospaces',
                                      '/usr/bin/env nospaces'),
                                     ('/usr/bin/env with spaces',
                                      '/usr/bin/env "with spaces"'),
                                     ('/usr/bin/env "pre spaced"',
                                      '/usr/bin/env "pre spaced"')):
            self.assertEqual(_enquote_executable(executable), expected)
Пример #26
0
class ScriptTestCase(unittest.TestCase):

    def setUp(self):
        source_dir = os.path.join(HERE, 'scripts')
        target_dir = tempfile.mkdtemp()
        self.maker = ScriptMaker(source_dir, target_dir, add_launchers=False)

    def tearDown(self):
        shutil.rmtree(self.maker.target_dir)

    @unittest.skipIf(sysconfig.is_python_build(), 'Test not appropriate for '
                     'Python source builds')
    def test_shebangs(self):
        executable = fsencode(get_executable())
        for fn in ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                   'shell.sh'):
            files = self.maker.make(fn)
            self.assertEqual(len(files), 1)
            d, f = os.path.split(files[0])
            self.assertEqual(f, fn)
            self.assertEqual(d, self.maker.target_dir)
            if fn.endswith('.py') and fn != 'foo.py':   # no shebang in foo.py
                with open(files[0], 'rb') as f:
                    first_line = f.readline()
                self.assertIn(executable, first_line)

    def test_shebangs_custom_executable(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=False)
        maker.executable = 'this_should_appear_in_the_shebang_line(中文)'
        # let's create the script to be copied. It has a vanilla shebang line,
        # with some Unicode in it.
        fn = os.path.join(srcdir, 'copied')
        with open(fn, 'w') as f:
            f.write(COPIED_SCRIPT)
        # Let's ask the maker to copy the script, and see what the shebang is
        # in the copy.
        filenames = maker.make('copied')
        with open(filenames[0], 'rb') as f:
            actual = f.readline().decode('utf-8')
        self.assertIn(maker.executable, actual)
        # Now let's make a script from a callable
        filenames = maker.make(MADE_SCRIPT)
        with open(filenames[0], 'rb') as f:
            actual = f.readline().decode('utf-8')
        self.assertIn(maker.executable, actual)

    def test_multiple(self):
        specs = ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                 'shell.sh', 'uwsgi_part')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        expected = set(specs)
        self.assertEqual(expected, set([os.path.basename(f) for f in files]))
        ofiles = os.listdir(self.maker.target_dir)
        self.assertEqual(expected, set(ofiles))

    def test_generation(self):
        self.maker.clobber = True
        for name in ('main', 'other_main'):
            for options in (None, {}, {'gui': False}, {'gui': True}):
                gui = options and options.get('gui', False)
                spec = 'foo = foo:' + name
                files = self.maker.make(spec, options)
                self.assertEqual(len(files), 2)
                actual = set()
                for f in files:
                    d, f = os.path.split(f)
                    actual.add(f)
                if os.name == 'nt':  # pragma: no cover
                    if gui:
                        ext = 'pyw'
                    else:
                        ext = 'py'
                    expected = set(['foo.%s' % ext,
                                    'foo-%s.%s' % (sys.version[:3], ext)])
                else:
                    expected = set(['foo', 'foo-%s' % sys.version[:3]])
                self.assertEqual(actual, expected)
                self.assertEqual(d, self.maker.target_dir)
                for fn in files:
                    with open(fn, 'r') as f:
                        text = f.read()
                    self.assertIn("_resolve('foo', '%s')" % name, text)
                    if options and options['gui'] and os.name == 'nt':  # pragma: no cover
                        first_line, rest = text.split('\n', 1)
                        self.assertIn('pythonw', first_line)

    def test_clobber(self):
        files = self.maker.make('foo = foo:main')
        saved_files = files
        self.assertGreaterEqual(len(files), 2)  # foo, foo-X.Y
        files = self.maker.make('foo = foo:main')
        self.assertFalse(files)
        self.maker.clobber = True
        files = self.maker.make('foo = foo:main')
        self.assertEqual(files, saved_files)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launchers(self):  # pragma: no cover
        tlauncher = self.maker._get_launcher('t')
        self.maker.add_launchers = True
        specs = ('foo.py', 'script1.py', 'script2.py', 'script3.py',
                 'shell.sh')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(('foo.py', 'script1.exe',
                                         'script2.exe', 'script3.exe',
                                         'shell.sh')))
        for fn in files:
            if not fn.endswith('.exe'):
                continue
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(tlauncher))

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launcher_run(self):
        self.maker.add_launchers = True
        files = self.maker.make('script6.py')
        self.assertEqual(len(files), 1)
        p = subprocess.Popen([files[0], 'Test Argument'],
                             stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate('input'.encode('ascii'))
        actual = stdout.decode('ascii').replace('\r\n', '\n')
        expected = textwrap.dedent("""
            script6.exe
            ['Test Argument']
            'input'
            non-optimized
            """).lstrip()
        self.assertEqual(actual, expected)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_launcher_run_with_interpreter_args(self):
        srcdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, srcdir)
        dstdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, dstdir)
        maker = ScriptMaker(srcdir, dstdir, add_launchers=True)

        # add '-O' option to shebang to run in optimized mode
        with open(os.path.join(HERE, 'scripts', 'script6.py'), 'r') as src:
            with open(os.path.join(srcdir, 'script6-optimized.py'), 'w') as dst:
                shebang = src.readline().rstrip()
                dst.write(shebang + " -O\n")
                dst.write(src.read())

        files = maker.make('script6-optimized.py')
        self.assertEqual(len(files), 1)
        p = subprocess.Popen([files[0], 'Test Argument'],
                             stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate('input'.encode('ascii'))
        actual = stdout.decode('ascii').replace('\r\n', '\n')
        expected = textwrap.dedent("""
            script6-optimized.exe
            ['Test Argument']
            'input'
            """).lstrip()  # 'non-optimized' is not printed this time
        self.assertEqual(actual, expected)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_windows(self):  # pragma: no cover
        wlauncher = self.maker._get_launcher('w')
        tlauncher = self.maker._get_launcher('t')
        self.maker.add_launchers = True
        executable = os.path.normcase(sys.executable).encode('utf-8')
        wexecutable = executable.replace(b'python.exe', b'pythonw.exe')
        files = self.maker.make('script4.py')
        self.assertEqual(len(files), 1)
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(['script4.exe']))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(wlauncher))
            self.assertIn(executable, data)
        # Now test making scripts gui and console
        files = self.maker.make('foo = foo:main', {'gui': True})
        self.assertEqual(len(files), 2)
        filenames = set([os.path.basename(f) for f in files])
        specific = sys.version[:3]
        self.assertEqual(filenames, set(('foo.exe', 'foo-%s.exe' % specific)))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(wlauncher))
            self.assertIn(wexecutable, data)

        files = self.maker.make('foo = foo:main')
        self.assertEqual(len(files), 2)
        filenames = set([os.path.basename(f) for f in files])
        self.assertEqual(filenames, set(('foo.exe', 'foo-%s.exe' % specific)))
        for fn in files:
            with open(fn, 'rb') as f:
                data = f.read()
            self.assertTrue(data.startswith(tlauncher))
            self.assertIn(executable, data)

    @unittest.skipIf(os.name != 'nt', 'Test is Windows-specific')
    def test_windows_run(self):
        self.maker.add_launchers = True
        files = self.maker.make('script7.pyw')
        self.assertEqual(len(files), 1)

        test_output = os.path.join(self.maker.target_dir, 'test_output.txt')
        p = subprocess.Popen([files[0], test_output, 'Test Argument'],
                             stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        stdout, stderr = p.communicate()
        self.assertFalse(stdout)
        self.assertFalse(stderr)
        with open(test_output, 'rb') as f:
            actual = f.read().decode('ascii')
        self.assertEqual(actual, 'Test Argument')

    def test_dry_run(self):
        self.maker.dry_run = True
        self.maker.variants = set([''])
        specs = ('foo.py', 'bar = foo:main')
        files = self.maker.make_multiple(specs)
        self.assertEqual(len(specs), len(files))
        if os.name == 'nt':  # pragma: no cover
            bar = 'bar.py'
        else:
            bar = 'bar'
        self.assertEqual(set(('foo.py', bar)),
                         set([os.path.basename(f) for f in files]))
        ofiles = os.listdir(self.maker.target_dir)
        self.assertFalse(ofiles)

    def test_script_run(self):
        files = self.maker.make('test = cgi:print_directory')
        self.assertEqual(len(files), 2)
        p = subprocess.Popen([sys.executable, files[0]],
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = p.communicate()
        self.assertIn(b'<H3>Current Working Directory:</H3>', stdout)
        self.assertIn(os.getcwd().encode('utf-8'), stdout)

    @unittest.skipUnless(os.name == 'posix', 'Test only valid for POSIX')
    def test_mode(self):
        self.maker.set_mode = False
        files = self.maker.make('foo = foo:main')
        self.assertEqual(len(files), 2)
        for f in files:
            self.assertIn(os.stat(f).st_mode & 0o7777, (0o644, 0o664))
        self.maker.set_mode = True
        files = self.maker.make('bar = bar:main')
        self.assertEqual(len(files), 2)
        for f in files:
            self.assertIn(os.stat(f).st_mode & 0o7777, (0o755, 0o775))

    def test_interpreter_args(self):
        executable = fsencode(get_executable())
        options = {
            'interpreter_args': ['-E', '"foo bar"', 'baz frobozz']
        }
        self.maker.variants = set([''])
        files = self.maker.make('foo = bar:baz', options=options)
        self.assertEqual(len(files), 1)
        with open(files[0], 'rb') as f:
            shebang_line = f.readline()
        if not sysconfig.is_python_build():
            self.assertIn(executable, shebang_line)
        self.assertIn(b' -E "foo bar" baz frobozz', shebang_line)

    def test_args_on_copy(self):
        self.maker.variants = set([''])
        self.maker.executable = 'mypython'
        files = self.maker.make('script5.py')
        with open(files[0]) as f:
            actual = f.readline().strip()
        self.assertEqual(actual, '#!mypython -mzippy.activate')
        if not sysconfig.is_python_build():
            self.maker.executable = None
            os.remove(files[0])
            files = self.maker.make('script5.py')
            with open(files[0]) as f:
                actual = f.readline().strip()
            expected = '#!%s -mzippy.activate' % get_executable()
            self.assertEqual(actual, expected)

    def test_enquote_executable(self):
        for executable, expected in (
                ('/no/spaces', '/no/spaces'),
                ('/i have/space', '"/i have/space"'),
                ('"/space prequoted"', '"/space prequoted"'),
                ('/usr/bin/env nospaces', '/usr/bin/env nospaces'),
                ('/usr/bin/env with spaces', '/usr/bin/env "with spaces"'),
                ('/usr/bin/env "pre spaced"', '/usr/bin/env "pre spaced"')
                ):
            self.assertEqual(_enquote_executable(executable),
                             expected)
Пример #27
0
def move_wheel_files(name,
                     req,
                     wheeldir,
                     user=False,
                     home=None,
                     root=None,
                     pycompile=True,
                     scheme=None):
    """Install a wheel"""

    if not scheme:
        scheme = distutils_scheme(name, user=user, home=home, root=root)

    if root_is_purelib(name, wheeldir):
        lib_dir = scheme['purelib']
    else:
        lib_dir = scheme['platlib']

    info_dir = []
    data_dirs = []
    source = wheeldir.rstrip(os.path.sep) + os.path.sep

    # Record details of the files moved
    #   installed = files copied from the wheel to the destination
    #   changed = files changed while installing (scripts #! line typically)
    #   generated = files newly generated during the install (script wrappers)
    installed = {}
    changed = set()
    generated = []

    # Compile all of the pyc files that we're going to be installing
    if pycompile:
        compileall.compile_dir(source, force=True, quiet=True)

    def normpath(src, p):
        return make_path_relative(src, p).replace(os.path.sep, '/')

    def record_installed(srcfile, destfile, modified=False):
        """Map archive RECORD paths to installation RECORD paths."""
        oldpath = normpath(srcfile, wheeldir)
        newpath = normpath(destfile, lib_dir)
        installed[oldpath] = newpath
        if modified:
            changed.add(destfile)

    def clobber(source, dest, is_base, fixer=None, filter=None):
        if not os.path.exists(dest):  # common for the 'include' path
            os.makedirs(dest)

        for dir, subdirs, files in os.walk(source):
            basedir = dir[len(source):].lstrip(os.path.sep)
            destdir = os.path.join(dest, basedir)
            if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'):
                continue
            for s in subdirs:
                destsubdir = os.path.join(dest, basedir, s)
                if is_base and basedir == '' and destsubdir.endswith('.data'):
                    data_dirs.append(s)
                    continue
                elif (is_base and s.endswith('.dist-info')
                      # is self.req.project_name case preserving?
                      and s.lower().startswith(
                          req.project_name.replace('-', '_').lower())):
                    assert not info_dir, 'Multiple .dist-info directories'
                    info_dir.append(destsubdir)
            for f in files:
                # Skip unwanted files
                if filter and filter(f):
                    continue
                srcfile = os.path.join(dir, f)
                destfile = os.path.join(dest, basedir, f)
                # directory creation is lazy and after the file filtering above
                # to ensure we don't install empty dirs; empty dirs can't be
                # uninstalled.
                if not os.path.exists(destdir):
                    os.makedirs(destdir)
                # use copy2 (not move) to be extra sure we're not moving
                # directories over; copy2 fails for directories.  this would
                # fail tests (not during released/user execution)
                shutil.copy2(srcfile, destfile)
                changed = False
                if fixer:
                    changed = fixer(destfile)
                record_installed(srcfile, destfile, changed)

    clobber(source, lib_dir, True)

    assert info_dir, "%s .dist-info directory not found" % req

    # Get the defined entry points
    ep_file = os.path.join(info_dir[0], 'entry_points.txt')
    console, gui = get_entrypoints(ep_file)

    def is_entrypoint_wrapper(name):
        # EP, EP.exe and EP-script.py are scripts generated for
        # entry point EP by setuptools
        if name.lower().endswith('.exe'):
            matchname = name[:-4]
        elif name.lower().endswith('-script.py'):
            matchname = name[:-10]
        elif name.lower().endswith(".pya"):
            matchname = name[:-4]
        else:
            matchname = name
        # Ignore setuptools-generated scripts
        return (matchname in console or matchname in gui)

    for datadir in data_dirs:
        fixer = None
        filter = None
        for subdir in os.listdir(os.path.join(wheeldir, datadir)):
            fixer = None
            if subdir == 'scripts':
                fixer = fix_script
                filter = is_entrypoint_wrapper
            source = os.path.join(wheeldir, datadir, subdir)
            dest = scheme[subdir]
            clobber(source, dest, False, fixer=fixer, filter=filter)

    maker = ScriptMaker(None, scheme['scripts'])

    # Ensure we don't generate any variants for scripts because this is almost
    # never what somebody wants.
    # See https://bitbucket.org/pypa/distlib/issue/35/
    maker.variants = set(('', ))

    # This is required because otherwise distlib creates scripts that are not
    # executable.
    # See https://bitbucket.org/pypa/distlib/issue/32/
    maker.set_mode = True

    # Simplify the script and fix the fact that the default script swallows
    # every single stack trace.
    # See https://bitbucket.org/pypa/distlib/issue/34/
    # See https://bitbucket.org/pypa/distlib/issue/33/
    def _get_script_text(entry):
        return maker.script_template % {
            "module": entry.prefix,
            "import_name": entry.suffix.split(".")[0],
            "func": entry.suffix,
        }

    maker._get_script_text = _get_script_text
    maker.script_template = """# -*- coding: utf-8 -*-
import re
import sys

from %(module)s import %(import_name)s

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(%(func)s())
"""

    # Special case pip and setuptools to generate versioned wrappers
    #
    # The issue is that some projects (specifically, pip and setuptools) use
    # code in setup.py to create "versioned" entry points - pip2.7 on Python
    # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into
    # the wheel metadata at build time, and so if the wheel is installed with
    # a *different* version of Python the entry points will be wrong. The
    # correct fix for this is to enhance the metadata to be able to describe
    # such versioned entry points, but that won't happen till Metadata 2.0 is
    # available.
    # In the meantime, projects using versioned entry points will either have
    # incorrect versioned entry points, or they will not be able to distribute
    # "universal" wheels (i.e., they will need a wheel per Python version).
    #
    # Because setuptools and pip are bundled with _ensurepip and virtualenv,
    # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we
    # override the versioned entry points in the wheel and generate the
    # correct ones. This code is purely a short-term measure until Metadat 2.0
    # is available.
    #
    # To add the level of hack in this section of code, in order to support
    # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment
    # variable which will control which version scripts get installed.
    #
    # ENSUREPIP_OPTIONS=altinstall
    #   - Only pipX.Y and easy_install-X.Y will be generated and installed
    # ENSUREPIP_OPTIONS=install
    #   - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note
    #     that this option is technically if ENSUREPIP_OPTIONS is set and is
    #     not altinstall
    # DEFAULT
    #   - The default behavior is to install pip, pipX, pipX.Y, easy_install
    #     and easy_install-X.Y.
    pip_script = console.pop('pip', None)
    if pip_script:
        if "ENSUREPIP_OPTIONS" not in os.environ:
            spec = 'pip = ' + pip_script
            generated.extend(maker.make(spec))

        if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall":
            spec = 'pip%s = %s' % (sys.version[:1], pip_script)
            generated.extend(maker.make(spec))

        spec = 'pip%s = %s' % (sys.version[:3], pip_script)
        generated.extend(maker.make(spec))
        # Delete any other versioned pip entry points
        pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)]
        for k in pip_ep:
            del console[k]
    easy_install_script = console.pop('easy_install', None)
    if easy_install_script:
        if "ENSUREPIP_OPTIONS" not in os.environ:
            spec = 'easy_install = ' + easy_install_script
            generated.extend(maker.make(spec))

        spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script)
        generated.extend(maker.make(spec))
        # Delete any other versioned easy_install entry points
        easy_install_ep = [
            k for k in console if re.match(r'easy_install(-\d\.\d)?$', k)
        ]
        for k in easy_install_ep:
            del console[k]

    # Generate the console and GUI entry points specified in the wheel
    if len(console) > 0:
        generated.extend(
            maker.make_multiple(['%s = %s' % kv for kv in console.items()]))
    if len(gui) > 0:
        generated.extend(
            maker.make_multiple(['%s = %s' % kv for kv in gui.items()],
                                {'gui': True}))

    record = os.path.join(info_dir[0], 'RECORD')
    temp_record = os.path.join(info_dir[0], 'RECORD.pip')
    with open_for_csv(record, 'r') as record_in:
        with open_for_csv(temp_record, 'w+') as record_out:
            reader = csv.reader(record_in)
            writer = csv.writer(record_out)
            for row in reader:
                row[0] = installed.pop(row[0], row[0])
                if row[0] in changed:
                    row[1], row[2] = rehash(row[0])
                writer.writerow(row)
            for f in generated:
                h, l = rehash(f)
                writer.writerow((f, h, l))
            for f in installed:
                writer.writerow((installed[f], '', ''))
    shutil.move(temp_record, record)
Пример #28
0
        # Ignore setuptools-generated scripts
        return (matchname in console or matchname in gui)

    for datadir in data_dirs:
        fixer = None
        filter = None
        for subdir in os.listdir(os.path.join(wheeldir, datadir)):
            fixer = None
            if subdir == 'scripts':
                fixer = fix_script
                filter = is_entrypoint_wrapper
            source = os.path.join(wheeldir, datadir, subdir)
            dest = scheme[subdir]
            clobber(source, dest, False, fixer=fixer, filter=filter)

    maker = ScriptMaker(None, scheme['scripts'])

    # Ensure we don't generate any variants for scripts because this is almost
    # never what somebody wants.
    # See https://bitbucket.org/pypa/distlib/issue/35/
    maker.variants = set(('', ))

    # This is required because otherwise distlib creates scripts that are not
    # executable.
    # See https://bitbucket.org/pypa/distlib/issue/32/
    maker.set_mode = True

    # Simplify the script and fix the fact that the default script swallows
    # every single stack trace.
    # See https://bitbucket.org/pypa/distlib/issue/34/
    # See https://bitbucket.org/pypa/distlib/issue/33/
Пример #29
0
def _install_wheels(need, system):
	# TODO: if a package is being updated, it should be installed in
	# the same location as before

	# Find all packages that should be deleted
	all = dict([ (d.name, d)
		for d in installed_core() | installed_tools() | installed_toolboxes() ])
	from distlib.database import make_graph
	import itertools
	graph = make_graph(itertools.chain(all.values(), need))
	l = need[:]	# what we started with
	ordered = []	# ordered by least dependency
	depend = {}	# dependency relationship cache
	while l:
		for d in l:
			for d2 in l:
				if d2 is d:
					continue
				try:
					dep = depend[(d, d2)]
				except KeyError:
					dep = _depends_on(graph, d, d2)
					depend[(d, d2)] = dep
				if dep:
					break
			else:
				ordered.append(d)
				l.remove(d)
				break
		else:
			# This can only happen if there is circular dependencies
			# in which case we just process the distributions in
			# given order since its no worse than anything else
			ordered.extend(l)
			break
	remove_list = []
	check = set()
	for d in ordered:
		if d in remove_list:
			continue
		try:
			rd = all[d.name]
		except KeyError:
			pass
		else:
			remove_list.append(rd)
			al = graph.adjacency_list[rd]
			if al:
				check.update([ sd for sd, l in al ])
	# Repeatedly go through the list of distributions to see whether
	# they can be removed.  It must be iterative.  Suppose A and B need
	# to be removed; C depends on A; D depends on B and C; if we check D
	# first, it will not be removable since C is not marked for removal
	# yet; but a second pass will show that D is removable.  Iteration
	# ends when no new packages are marked as removable.
	while check:
		any_deletion = False
		new_check = set()
		for d in check:
			for pd in graph.reverse_list[d]:
				if pd not in remove_list:
					new_check.add(d)
					break
			else:
				any_deletion = True
				remove_list.append(d)
				for sd, l in graph.adjacency_list[d]:
					if sd not in remove_list and sd not in check:
						new_check.add(sd)
		if not any_deletion:
			break
		check = new_check
	removed_location = {}
	for d in remove_list:
		removed_location[d.name] = _remove_distribution(d)

	dl = download_location()
	default_paths = _install_make_paths(system)
	from distlib.scripts import ScriptMaker
	maker = ScriptMaker(None, None)
	import os.path
	try:
		from urllib.request import urlretrieve, URLError
	except ImportError:
		from urllib import urlretrieve, URLError
	from distlib.wheel import Wheel
	from distlib import DistlibException
	for d in need:
		try:
			old_location = removed_location[d.name]
		except KeyError:
			paths = default_paths
		else:
			paths = _install_make_paths(system, old_location)
		url = d.source_url
		filename = url.split('/')[-1]
		dloc = os.path.join(dl, filename)
		if not os.path.isfile(dloc):
			try:
				filename, headers = urlretrieve(url, dloc)
			except URLError as e:
				show("Warning: cannot fetch %s: %s" % (url, str(e)))
				continue
		w = Wheel(dloc)
		try:
			w.verify()
		except DistlibExecption as e:
			show("Warning: cannot verify %s: %s" % (d.name, str(e)))
			continue
		show("installing %s (%s)" % (w.name, w.version))
		w.install(paths, maker)