From 0569324855d9d1e5efe6e9527d4c649a3472a82b Mon Sep 17 00:00:00 2001 From: Marcus Smith Date: Sat, 18 Aug 2012 15:26:17 -0700 Subject: [PATCH] handle __pycache__ file removal --- pip/backwardcompat.py | 3 +++ pip/req.py | 17 ++++++++--------- pip/util.py | 1 - tests/test_uninstall.py | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pip/backwardcompat.py b/pip/backwardcompat.py index 7f81c370d..788023fae 100644 --- a/pip/backwardcompat.py +++ b/pip/backwardcompat.py @@ -1,11 +1,14 @@ """Stuff that differs in different Python versions""" import os +import imp import sys import site __all__ = ['WindowsError'] +uses_pycache = hasattr(imp,'cache_from_source') + try: WindowsError = WindowsError except NameError: diff --git a/pip/req.py b/pip/req.py index f413b28e2..4179f7ad8 100644 --- a/pip/req.py +++ b/pip/req.py @@ -1,5 +1,6 @@ from email.parser import FeedParser import os +import imp import pkg_resources import re import sys @@ -13,13 +14,13 @@ from pip.exceptions import (InstallationError, UninstallationError, DistributionNotFound) from pip.vcs import vcs from pip.log import logger -from pip.util import display_path, rmtree, is_pypy +from pip.util import display_path, rmtree from pip.util import ask, ask_path_exists, backup_dir from pip.util import is_installable_dir, is_local, dist_is_local, dist_in_usersite from pip.util import renames, normalize_path, egg_link_path, dist_in_site_packages from pip.util import make_path_relative from pip.util import call_subprocess -from pip.backwardcompat import (urlparse, urllib, +from pip.backwardcompat import (urlparse, urllib, uses_pycache, ConfigParser, string_types, HTTPError, get_python_version, b) from pip.index import Link @@ -448,6 +449,8 @@ exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec')) for installed_file in dist.get_metadata('installed-files.txt').splitlines(): path = os.path.normpath(os.path.join(egg_info_path, installed_file)) paths_to_remove.add(path) + #FIXME: need a test for this elif block + #occurs with --single-version-externally-managed/--record outside of pip elif dist.has_metadata('top_level.txt'): if dist.has_metadata('namespace_packages.txt'): namespaces = dist.get_metadata('namespace_packages.txt') @@ -1435,13 +1438,9 @@ class UninstallPathSet(object): else: self._refuse.add(path) - #workaround for pip issue #626 (debian pypy creates __pycache__ folders) - if is_pypy: - head, tail = os.path.split(path) - tail_root, tail_ext = os.path.splitext(tail) - if tail_ext == '.py': - pycache_path = os.path.join(head, '__pycache__', tail_root + '.pypy-%d%d.pyc' % sys.pypy_version_info[:2]) - self.add(pycache_path) + # __pycache__ files can show up after 'installed-files.txt' is created, due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(imp.cache_from_source(path)) def add_pth(self, pth_file, entry): diff --git a/pip/util.py b/pip/util.py index 8237db78a..833a7adcc 100644 --- a/pip/util.py +++ b/pip/util.py @@ -172,7 +172,6 @@ def is_svn_page(html): return (re.search(r'[^<]*Revision \d+:', html) and re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) -is_pypy = hasattr(sys, 'pypy_version_info') def file_contents(filename): fp = open(filename, 'rb') diff --git a/tests/test_uninstall.py b/tests/test_uninstall.py index 408847487..28339a615 100644 --- a/tests/test_uninstall.py +++ b/tests/test_uninstall.py @@ -18,6 +18,8 @@ def test_simple_uninstall(): env = reset_env() result = run_pip('install', 'INITools==0.2') assert join(env.site_packages, 'initools') in result.files_created, sorted(result.files_created.keys()) + #the import forces the generation of __pycache__ if the version of python supports it + env.run('python', '-c', "import initools") result2 = run_pip('uninstall', 'INITools', '-y') assert_all_changes(result, result2, [env.venv/'build', 'cache']) @@ -36,6 +38,19 @@ def test_uninstall_with_scripts(): assert_all_changes(result, result2, [env.venv/'build', 'cache']) +def test_uninstall_easy_install_after_import(): + """ + Uninstall an easy_installed package after it's been imported + + """ + env = reset_env() + result = env.run('easy_install', 'INITools==0.2', expect_stderr=True) + #the import forces the generation of __pycache__ if the version of python supports it + env.run('python', '-c', "import initools") + result2 = run_pip('uninstall', 'INITools', '-y') + assert_all_changes(result, result2, [env.venv/'build', 'cache']) + + def test_uninstall_namespace_package(): """ Uninstall a distribution with a namespace package without clobbering @@ -66,6 +81,8 @@ def test_uninstall_overlapping_package(): result2 = run_pip('install', child_pkg, expect_error=False) assert join(env.site_packages, 'child') in result2.files_created, sorted(result2.files_created.keys()) assert normpath(join(env.site_packages, 'parent/plugins/child_plugin.py')) in result2.files_created, sorted(result2.files_created.keys()) + #the import forces the generation of __pycache__ if the version of python supports it + env.run('python', '-c', "import parent.plugins.child_plugin, child") result3 = run_pip('uninstall', '-y', 'child', expect_error=False) assert join(env.site_packages, 'child') in result3.files_deleted, sorted(result3.files_created.keys()) assert normpath(join(env.site_packages, 'parent/plugins/child_plugin.py')) in result3.files_deleted, sorted(result3.files_deleted.keys())