From 526db7293aa439df63364a4dca7850670a9fd8dc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 23 Feb 2016 12:52:18 -0500 Subject: [PATCH] A better way to manage the Python packages to uninstall. --- pavelib/prereqs.py | 72 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/pavelib/prereqs.py b/pavelib/prereqs.py index cb0d60d62e..c7e25c6cb2 100644 --- a/pavelib/prereqs.py +++ b/pavelib/prereqs.py @@ -5,11 +5,12 @@ Install Python and Node prerequisites. from distutils import sysconfig import hashlib import os +import re +import sys from paver.easy import sh, task from .utils.envs import Env -import sys PREREQS_STATE_DIR = os.getenv('PREREQ_CACHE_DIR', Env.REPO_ROOT / '.prereqs_cache') @@ -151,6 +152,16 @@ def install_node_prereqs(): prereq_cache("Node prereqs", ["package.json"], node_prereqs_installation) +# To add a package to the uninstall list, just add it to this list! No need +# to touch any other part of this file. +PACKAGES_TO_UNINSTALL = [ + "South", # Because it interferes with Django 1.8 migrations. + "edxval", # Because it was bork-installed somehow. + "django-storages", + "django-oauth2-provider", # Because now it's called edx-django-oauth2-provider. +] + + @task def uninstall_python_packages(): """ @@ -162,14 +173,18 @@ def uninstall_python_packages(): them. """ - # So that we don't constantly uninstall things, use a version number of the - # uninstallation needs. Check it, and skip this if we're up to date. - expected_version = 3 - state_file_path = os.path.join(PREREQS_STATE_DIR, "python_uninstall_version.txt") + # So that we don't constantly uninstall things, use a hash of the packages + # to be uninstalled. Check it, and skip this if we're up to date. + hasher = hashlib.sha1() + hasher.update(repr(PACKAGES_TO_UNINSTALL)) + expected_version = hasher.hexdigest() + state_file_path = os.path.join(PREREQS_STATE_DIR, "Python_uninstall.sha1") + if os.path.isfile(state_file_path): with open(state_file_path) as state_file: - version = int(state_file.read()) + version = state_file.read() if version == expected_version: + print 'Python uninstalls unchanged, skipping...' return # Run pip to find the packages we need to get rid of. Believe it or not, @@ -177,27 +192,13 @@ def uninstall_python_packages(): # to really really get rid of it. for _ in range(3): uninstalled = False - frozen = sh("pip freeze", capture=True).splitlines() + frozen = sh("pip freeze", capture=True) - # Uninstall South - if any(line.startswith("South") for line in frozen): - sh("pip uninstall --disable-pip-version-check -y South") - uninstalled = True - - # Uninstall edx-val - if any("edxval" in line for line in frozen): - sh("pip uninstall --disable-pip-version-check -y edxval") - uninstalled = True - - # Uninstall django-storages - if any("django-storages==" in line for line in frozen): - sh("pip uninstall --disable-pip-version-check -y django-storages") - uninstalled = True - - # Uninstall django-oauth2-provider - if any(line.startswith("django-oauth2-provider==") for line in frozen): - sh("pip uninstall --disable-pip-version-check -y django-oauth2-provider") - uninstalled = True + for package_name in PACKAGES_TO_UNINSTALL: + if package_in_frozen(package_name, frozen): + # Uninstall the pacakge + sh("pip uninstall --disable-pip-version-check -y {}".format(package_name)) + uninstalled = True if not uninstalled: break @@ -208,7 +209,24 @@ def uninstall_python_packages(): # Write our version. with open(state_file_path, "w") as state_file: - state_file.write(str(expected_version)) + state_file.write(expected_version) + + +def package_in_frozen(package_name, frozen_output): + """Is this package in the output of 'pip freeze'?""" + # Look for either: + # + # PACKAGE-NAME== + # + # or: + # + # blah_blah#egg=package_name-version + # + pattern = r"(?mi)^{pkg}==|#egg={pkg_under}-".format( + pkg=re.escape(package_name), + pkg_under=re.escape(package_name.replace("-", "_")), + ) + return bool(re.search(pattern, frozen_output)) @task