[python-pip] Patch local dos with predictable temp directory names

Matej Stuchlik mstuchli at fedoraproject.org
Wed Nov 19 15:35:49 UTC 2014


commit a7c7b123d7d980c2f73096b9956f78c629fc301b
Author: Matej Stuchlik <mstuchli at redhat.com>
Date:   Tue Nov 18 13:49:10 2014 +0100

    Patch local dos with predictable temp directory names
    
    See more at http://seclists.org/oss-sec/2014/q4/655

 local-dos.patch |  395 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 python-pip.spec |   11 ++-
 2 files changed, 405 insertions(+), 1 deletions(-)
---
diff --git a/local-dos.patch b/local-dos.patch
new file mode 100644
index 0000000..721ca51
--- /dev/null
+++ b/local-dos.patch
@@ -0,0 +1,395 @@
+diff --git a/pip/cmdoptions.py b/pip/cmdoptions.py
+index 8ed3d91..01b2104 100644
+--- a/pip/cmdoptions.py
++++ b/pip/cmdoptions.py
+@@ -9,7 +9,7 @@ To be consistent, all options will follow this design.
+ """
+ import copy
+ from optparse import OptionGroup, SUPPRESS_HELP, Option
+-from pip.locations import build_prefix, default_log_file
++from pip.locations import default_log_file
+ 
+ 
+ def make_option_group(group, parser):
+@@ -297,10 +297,8 @@ build_dir = OptionMaker(
+     '-b', '--build', '--build-dir', '--build-directory',
+     dest='build_dir',
+     metavar='dir',
+-    default=build_prefix,
+-    help='Directory to unpack packages into and build in. '
+-    'The default in a virtualenv is "<venv path>/build". '
+-    'The default for global installs is "<OS temp dir>/pip_build_<username>".')
++    help='Directory to unpack packages into and build in.',
++)
+ 
+ install_options = OptionMaker(
+     '--install-option',
+diff --git a/pip/commands/install.py b/pip/commands/install.py
+index cbf22a0..cb7d0db 100644
+--- a/pip/commands/install.py
++++ b/pip/commands/install.py
+@@ -10,6 +10,7 @@ from pip.basecommand import Command
+ from pip.index import PackageFinder
+ from pip.exceptions import InstallationError, CommandError, PreviousBuildDirError
+ from pip import cmdoptions
++from pip.util import BuildDirectory
+ 
+ 
+ class InstallCommand(Command):
+@@ -188,7 +189,7 @@ class InstallCommand(Command):
+         if (
+             options.no_install or
+             options.no_download or
+-            (options.build_dir != build_prefix) or
++            options.build_dir or
+             options.no_clean
+         ):
+             logger.deprecated('1.7', 'DEPRECATION: --no-install, --no-download, --build, '
+@@ -197,7 +198,16 @@ class InstallCommand(Command):
+         if options.download_dir:
+             options.no_install = True
+             options.ignore_installed = True
+-        options.build_dir = os.path.abspath(options.build_dir)
++
++        # If we have --no-install or --no-download and no --build we use the
++        # legacy static build dir
++        if (options.build_dir is None
++                and (options.no_install or options.no_download)):
++            options.build_dir = build_prefix
++
++        if options.build_dir:
++            options.build_dir = os.path.abspath(options.build_dir)
++
+         options.src_dir = os.path.abspath(options.src_dir)
+         install_options = options.install_options or []
+         if options.use_user_site:
+@@ -246,73 +256,75 @@ class InstallCommand(Command):
+ 
+         finder = self._build_package_finder(options, index_urls, session)
+ 
+-        requirement_set = RequirementSet(
+-            build_dir=options.build_dir,
+-            src_dir=options.src_dir,
+-            download_dir=options.download_dir,
+-            download_cache=options.download_cache,
+-            upgrade=options.upgrade,
+-            as_egg=options.as_egg,
+-            ignore_installed=options.ignore_installed,
+-            ignore_dependencies=options.ignore_dependencies,
+-            force_reinstall=options.force_reinstall,
+-            use_user_site=options.use_user_site,
+-            target_dir=temp_target_dir,
+-            session=session,
+-            pycompile=options.compile,
+-        )
+-        for name in args:
+-            requirement_set.add_requirement(
+-                InstallRequirement.from_line(name, None))
+-        for name in options.editables:
+-            requirement_set.add_requirement(
+-                InstallRequirement.from_editable(name, default_vcs=options.default_vcs))
+-        for filename in options.requirements:
+-            for req in parse_requirements(filename, finder=finder, options=options, session=session):
+-                requirement_set.add_requirement(req)
+-        if not requirement_set.has_requirements:
+-            opts = {'name': self.name}
+-            if options.find_links:
+-                msg = ('You must give at least one requirement to %(name)s '
+-                       '(maybe you meant "pip %(name)s %(links)s"?)' %
+-                       dict(opts, links=' '.join(options.find_links)))
+-            else:
+-                msg = ('You must give at least one requirement '
+-                       'to %(name)s (see "pip help %(name)s")' % opts)
+-            logger.warn(msg)
+-            return
+-
+-        try:
+-            if not options.no_download:
+-                requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
+-            else:
+-                requirement_set.locate_files()
+-
+-            if not options.no_install and not self.bundle:
+-                requirement_set.install(
+-                    install_options,
+-                    global_options,
+-                    root=options.root_path,
+-                    strip_file_prefix=options.strip_file_prefix)
+-                installed = ' '.join([req.name for req in
+-                                      requirement_set.successfully_installed])
+-                if installed:
+-                    logger.notify('Successfully installed %s' % installed)
+-            elif not self.bundle:
+-                downloaded = ' '.join([req.name for req in
+-                                       requirement_set.successfully_downloaded])
+-                if downloaded:
+-                    logger.notify('Successfully downloaded %s' % downloaded)
+-            elif self.bundle:
+-                requirement_set.create_bundle(self.bundle_filename)
+-                logger.notify('Created bundle in %s' % self.bundle_filename)
+-        except PreviousBuildDirError:
+-            options.no_clean = True
+-            raise
+-        finally:
+-            # Clean up
+-            if (not options.no_clean) and ((not options.no_install) or options.download_dir):
+-                requirement_set.cleanup_files(bundle=self.bundle)
++        build_delete = (not (options.no_clean or options.build_dir))
++        with BuildDirectory(options.build_dir, delete=build_delete) as build_dir:
++            requirement_set = RequirementSet(
++                build_dir=build_dir,
++                src_dir=options.src_dir,
++                download_dir=options.download_dir,
++                download_cache=options.download_cache,
++                upgrade=options.upgrade,
++                as_egg=options.as_egg,
++                ignore_installed=options.ignore_installed,
++                ignore_dependencies=options.ignore_dependencies,
++                force_reinstall=options.force_reinstall,
++                use_user_site=options.use_user_site,
++                target_dir=temp_target_dir,
++                session=session,
++                pycompile=options.compile,
++            )
++            for name in args:
++                requirement_set.add_requirement(
++                    InstallRequirement.from_line(name, None))
++            for name in options.editables:
++                requirement_set.add_requirement(
++                    InstallRequirement.from_editable(name, default_vcs=options.default_vcs))
++            for filename in options.requirements:
++                for req in parse_requirements(filename, finder=finder, options=options, session=session):
++                    requirement_set.add_requirement(req)
++            if not requirement_set.has_requirements:
++                opts = {'name': self.name}
++                if options.find_links:
++                    msg = ('You must give at least one requirement to %(name)s '
++                           '(maybe you meant "pip %(name)s %(links)s"?)' %
++                           dict(opts, links=' '.join(options.find_links)))
++                else:
++                    msg = ('You must give at least one requirement '
++                           'to %(name)s (see "pip help %(name)s")' % opts)
++                logger.warn(msg)
++                return
++
++            try:
++                if not options.no_download:
++                    requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
++                else:
++                    requirement_set.locate_files()
++
++                if not options.no_install and not self.bundle:
++                    requirement_set.install(
++                        install_options,
++                        global_options,
++                        root=options.root_path,
++                        strip_file_prefix=options.strip_file_prefix)
++                    installed = ' '.join([req.name for req in
++                                          requirement_set.successfully_installed])
++                    if installed:
++                        logger.notify('Successfully installed %s' % installed)
++                elif not self.bundle:
++                    downloaded = ' '.join([req.name for req in
++                                           requirement_set.successfully_downloaded])
++                    if downloaded:
++                        logger.notify('Successfully downloaded %s' % downloaded)
++                elif self.bundle:
++                    requirement_set.create_bundle(self.bundle_filename)
++                    logger.notify('Created bundle in %s' % self.bundle_filename)
++            except PreviousBuildDirError:
++                options.no_clean = True
++                raise
++            finally:
++                # Clean up
++                if (not options.no_clean) and ((not options.no_install) or options.download_dir):
++                    requirement_set.cleanup_files(bundle=self.bundle)
+ 
+         if options.target_dir:
+             if not os.path.exists(options.target_dir):
+diff --git a/pip/commands/wheel.py b/pip/commands/wheel.py
+index 6527063..a96631a 100644
+--- a/pip/commands/wheel.py
++++ b/pip/commands/wheel.py
+@@ -8,7 +8,7 @@ from pip.index import PackageFinder
+ from pip.log import logger
+ from pip.exceptions import CommandError, PreviousBuildDirError
+ from pip.req import InstallRequirement, RequirementSet, parse_requirements
+-from pip.util import normalize_path
++from pip.util import BuildDirectory, normalize_path
+ from pip.wheel import WheelBuilder
+ from pip import cmdoptions
+ 
+@@ -123,6 +123,9 @@ class WheelCommand(Command):
+                         "--extra-index-url is suggested.")
+             index_urls += options.mirrors
+ 
++        if options.build_dir:
++            options.build_dir = os.path.abspath(options.build_dir)
++
+         session = self._build_session(options)
+ 
+         finder = PackageFinder(find_links=options.find_links,
+@@ -137,59 +140,60 @@ class WheelCommand(Command):
+                                session=session,
+                             )
+ 
+-        options.build_dir = os.path.abspath(options.build_dir)
+-        requirement_set = RequirementSet(
+-            build_dir=options.build_dir,
+-            src_dir=None,
+-            download_dir=None,
+-            download_cache=options.download_cache,
+-            ignore_dependencies=options.ignore_dependencies,
+-            ignore_installed=True,
+-            session=session,
+-            wheel_download_dir=options.wheel_dir
+-        )
+-
+-        # make the wheelhouse
+-        if not os.path.exists(options.wheel_dir):
+-            os.makedirs(options.wheel_dir)
+-
+-        #parse args and/or requirements files
+-        for name in args:
+-            requirement_set.add_requirement(
+-                InstallRequirement.from_line(name, None))
+-
+-        for filename in options.requirements:
+-            for req in parse_requirements(
+-                filename,
+-                finder=finder,
+-                options=options,
+-                session=session):
+-                if req.editable:
+-                    logger.notify("ignoring %s" % req.url)
+-                    continue
+-                requirement_set.add_requirement(req)
+-
+-        #fail if no requirements
+-        if not requirement_set.has_requirements:
+-            opts = {'name': self.name}
+-            msg = ('You must give at least one requirement '
+-                   'to %(name)s (see "pip help %(name)s")' % opts)
+-            logger.error(msg)
+-            return
++        build_delete = (not (options.no_clean or options.build_dir))
++        with BuildDirectory(options.build_dir, delete=build_delete) as build_dir:
++            requirement_set = RequirementSet(
++                build_dir=build_dir,
++                src_dir=None,
++                download_dir=None,
++                download_cache=options.download_cache,
++                ignore_dependencies=options.ignore_dependencies,
++                ignore_installed=True,
++                session=session,
++                wheel_download_dir=options.wheel_dir
++            )
+ 
+-        try:
+-            #build wheels
+-            wb = WheelBuilder(
+-                requirement_set,
+-                finder,
+-                options.wheel_dir,
+-                build_options = options.build_options or [],
+-                global_options = options.global_options or []
+-                )
+-            wb.build()
+-        except PreviousBuildDirError:
+-            options.no_clean = True
+-            raise
+-        finally:
+-            if not options.no_clean:
+-                requirement_set.cleanup_files()
++            # make the wheelhouse
++            if not os.path.exists(options.wheel_dir):
++                os.makedirs(options.wheel_dir)
++
++            #parse args and/or requirements files
++            for name in args:
++                requirement_set.add_requirement(
++                    InstallRequirement.from_line(name, None))
++
++            for filename in options.requirements:
++                for req in parse_requirements(
++                    filename,
++                    finder=finder,
++                    options=options,
++                    session=session):
++                    if req.editable:
++                        logger.notify("ignoring %s" % req.url)
++                        continue
++                    requirement_set.add_requirement(req)
++
++            #fail if no requirements
++            if not requirement_set.has_requirements:
++                opts = {'name': self.name}
++                msg = ('You must give at least one requirement '
++                       'to %(name)s (see "pip help %(name)s")' % opts)
++                logger.error(msg)
++                return
++
++            try:
++                #build wheels
++                wb = WheelBuilder(
++                    requirement_set,
++                    finder,
++                    options.wheel_dir,
++                    build_options = options.build_options or [],
++                    global_options = options.global_options or []
++                    )
++                wb.build()
++            except PreviousBuildDirError:
++                options.no_clean = True
++                raise
++            finally:
++                if not options.no_clean:
++                    requirement_set.cleanup_files()
+diff --git a/pip/util.py b/pip/util.py
+index f459bb2..f5edeeb 100644
+--- a/pip/util.py
++++ b/pip/util.py
+@@ -8,6 +8,7 @@ import zipfile
+ import tarfile
+ import subprocess
+ import textwrap
++import tempfile
+ 
+ from pip.exceptions import InstallationError, BadCommand, PipError
+ from pip.backwardcompat import(WindowsError, string_types, raw_input,
+@@ -718,3 +719,35 @@ def is_prerelease(vers):
+ 
+     parsed = version._normalized_key(normalized)
+     return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed])
++
++
++class BuildDirectory(object):
++
++    def __init__(self, name=None, delete=None):
++        # If we were not given an explicit directory, and we were not given an
++        # explicit delete option, then we'll default to deleting.
++        if name is None and delete is None:
++            delete = True
++
++        if name is None:
++            name = tempfile.mkdtemp(prefix="pip-build-")
++            # If we were not given an explicit directory, and we were not given
++            # an explicit delete option, then we'll default to deleting.
++            if delete is None:
++                delete = True
++
++        self.name = name
++        self.delete = delete
++
++    def __repr__(self):
++        return "<{} {!r}>".format(self.__class__.__name__, self.name)
++
++    def __enter__(self):
++        return self.name
++
++    def __exit__(self, exc, value, tb):
++        self.cleanup()
++
++    def cleanup(self):
++        if self.delete:
++            rmtree(self.name)
diff --git a/python-pip.spec b/python-pip.spec
index d35b6a2..f4e0a28 100644
--- a/python-pip.spec
+++ b/python-pip.spec
@@ -16,7 +16,7 @@
 
 Name:           python-%{srcname}
 Version:        1.5.6
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        A tool for installing and managing Python packages
 
 Group:          Development/Libraries
@@ -24,6 +24,10 @@ License:        MIT
 URL:            http://www.pip-installer.org
 Source0:        http://pypi.python.org/packages/source/p/pip/%{srcname}-%{version}.tar.gz
 Patch0:         pip-1.5rc1-allow-stripping-prefix-from-wheel-RECORD-files.patch
+
+# patch by dstufft, more at http://seclists.org/oss-sec/2014/q4/655
+Patch1:         local-dos.patch
+
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 BuildArch:      noarch
@@ -66,6 +70,7 @@ easy_installable should be pip-installable as well.
 %setup -q -n %{srcname}-%{version}
 
 %patch0 -p1
+%patch1 -p1
 
 %{__sed} -i '1d' pip/__init__.py
 
@@ -136,6 +141,10 @@ pip2 install -I dist/%{python2_wheelname} --root %{buildroot} --strip-file-prefi
 %endif # with_python3
 
 %changelog
+* Tue Nov 18 2014 Matej Stuchlik <mstuchli at redhat.com> - 1.5.6-3
+- Added patch for local dos with predictable temp dictionary names
+  (http://seclists.org/oss-sec/2014/q4/655)
+
 * Sat Jun 07 2014 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 1.5.6-2
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
 


More information about the scm-commits mailing list