Commits

Hugo Lopes Tavares committed 17c2cc2

insert pip-delete-this-directory.txt in every build directory pip creates temporarily (it identifies pip created that), and always remove it if no longer needed. it fixes issue #57 and issue #133

  • Participants
  • Parent commits 8868a26

Comments (0)

Files changed (2)

 from pip import call_subprocess
 from pip.backwardcompat import any, md5, copytree
 from pip.index import Link
+from pip.locations import build_prefix
+
+
+PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt'
 
 
 class InstallRequirement(object):
             name = self.name
         # FIXME: Is there a better place to create the build_dir? (hg and bzr need this)
         if not os.path.exists(build_dir):
-            os.makedirs(build_dir)
+            _make_build_dir(build_dir)
         return os.path.join(build_dir, name)
 
     def correct_build_location(self):
         new_location = os.path.join(new_build_dir, name)
         if not os.path.exists(new_build_dir):
             logger.debug('Creating directory %s' % new_build_dir)
-            os.makedirs(new_build_dir)
+            _make_build_dir(new_build_dir)
         if os.path.exists(new_location):
             raise InstallationError(
                 'A package already exists in %s; please remove it to continue'
                     zipdir.external_attr = 0755 << 16L
                     zip.writestr(zipdir, '')
                 for filename in filenames:
-                    if filename == 'pip-delete-this-directory.txt':
+                    if filename ==  PIP_DELETE_MARKER_FILENAME:
                         continue
                     filename = os.path.join(dirpath, filename)
                     name = self._clean_zip_name(filename, dir)
     @property
     def delete_marker_filename(self):
         assert self.source_dir
-        return os.path.join(self.source_dir, 'pip-delete-this-directory.txt')
+        return os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)
 
 
 DELETE_MARKER_MESSAGE = '''\
                     else:
                         location = req_to_install.source_dir
                     if not os.path.exists(self.build_dir):
-                        os.makedirs(self.build_dir)
+                        _make_build_dir(self.build_dir)
                     req_to_install.update_editable(not self.is_download)
                     if self.is_download:
                         req_to_install.run_egg_info()
                                 # directory is created for packing in the bundle
                                 req_to_install.run_egg_info(force_root_egg_info=True)
                             req_to_install.assert_source_matches_version()
-                            f = open(req_to_install.delete_marker_filename, 'w')
-                            f.write(DELETE_MARKER_MESSAGE)
-                            f.close()
                             #@@ sketchy way of identifying packages not grabbed from an index
                             if bundle and req_to_install.url:
                                 self.copy_to_builddir(req_to_install)
         for req in self.reqs_to_cleanup:
             req.remove_temporary_source()
 
-        # The build dir can always be removed.
-        remove_dir = [self.build_dir]
+        remove_dir = []
+        if self._pip_has_created_build_dir():
+            remove_dir.append(self.build_dir)
 
         # The source dir of a bundle can always be removed.
         if bundle:
 
         logger.indent -= 2
 
+    def _pip_has_created_build_dir(self):
+        return (self.build_dir == build_prefix and
+                os.path.exists(os.path.join(self.build_dir, PIP_DELETE_MARKER_FILENAME)))
+
     def copy_to_builddir(self, req_to_install):
         target_dir = req_to_install.editable and self.src_dir or self.build_dir
         logger.info("Copying %s to %s" %(req_to_install.name, target_dir))
                     name = self._clean_zip_name(dirname, dir)
                     zip.writestr(basename + '/' + name + '/', '')
                 for filename in filenames:
-                    if filename == 'pip-delete-this-directory.txt':
+                    if filename == PIP_DELETE_MARKER_FILENAME:
                         continue
                     filename = os.path.join(dirpath, filename)
                     name = self._clean_zip_name(filename, dir)
         return name
 
 
+def _make_build_dir(build_dir):
+    os.makedirs(build_dir)
+    _write_delete_marker_message(os.path.join(build_dir, PIP_DELETE_MARKER_FILENAME))
+
+
+def _write_delete_marker_message(filepath):
+    marker_fp = open(filepath, 'w')
+    marker_fp.write(DELETE_MARKER_MESSAGE)
+    marker_fp.close()
+
+
 _scheme_re = re.compile(r'^(http|https|file):', re.I)
 
 

File tests/test_cleanup.py

+import os
 import textwrap
 from os.path import abspath, exists, join
-from test_pip import here, reset_env, run_pip, write_file
+from test_pip import here, reset_env, run_pip, write_file, mkdir
 from local_repos import local_checkout
+from path import Path
 
 
 def test_cleanup_after_install_from_pypi():
 
     # Make sure previously created src/ from editable still exists
     assert exists(src), "expected src dir doesn't exist: %s" % src
+
+
+def test_no_install_and_download_should_not_leave_build_dir():
+    """
+    It should remove build/ dir if it was pip that created
+    """
+    env = reset_env()
+    mkdir('downloaded_packages')
+    assert not os.path.exists(env.venv_path/'/build')
+    result = run_pip('install', '--no-install', 'INITools==0.2', '-d', 'downloaded_packages')
+    assert Path('scratch')/'downloaded_packages/build' not in result.files_created, 'pip should not leave build/ dir'
+    assert not os.path.exists(env.venv_path/'/build'), "build/ dir should be deleted"
+
+
+def test_download_should_not_delete_existing_build_dir():
+    """
+    It should not delete build/ if existing before run the command
+    """
+    env = reset_env()
+    mkdir(env.venv_path/'build')
+    f = open(env.venv_path/'build'/'somefile.txt', 'w')
+    f.write('I am not empty!')
+    f.close()
+    run_pip('install', '--no-install', 'INITools==0.2', '-d', '.')
+    f = open(env.venv_path/'build'/'somefile.txt')
+    content = f.read()
+    f.close()
+    assert os.path.exists(env.venv_path/'build'), "build/ should be left if it exists before pip run"
+    assert content == 'I am not empty!', "it should not affect build/ and its content"
+    assert ['somefile.txt'] == os.listdir(env.venv_path/'build')