Source code for msl.package_manager.uninstall

"""
Uninstall MSL packages.
"""
import os
import subprocess
import sys

import pkg_resources

from . import utils


[docs]def uninstall(*names, **kwargs): """Uninstall MSL packages. .. versionchanged:: 2.4.0 Added the `pip_options` keyword argument. Parameters ---------- *names The name(s) of the MSL package(s) to uninstall. If not specified then uninstall all MSL packages (except for the **MSL Package Manager** -- in which case use ``pip uninstall msl-package-manager``). The ``msl-`` prefix can be omitted (e.g., ``'loadlib'`` is equivalent to ``'msl-loadlib'``). Also accepts shell-style wildcards (e.g., ``'pr-*'``). **kwargs * yes -- :class:`bool` If :data:`True` then don't ask for confirmation before uninstalling. The default is :data:`False` (ask before uninstalling). * pip_options -- :class:`list` of :class:`str` Optional arguments to pass to the ``pip uninstall`` command, e.g., ``['--no-python-version-warning']`` """ # TODO Python 2.7 does not support named arguments after using *args # we can define yes=False, pip_options=None in the function signature # when we choose to drop support for Python 2.7 utils._check_kwargs(kwargs, {'yes', 'pip_options'}) yes = kwargs.get('yes', False) pip_options = kwargs.get('pip_options', []) packages = utils._create_uninstall_list(names) if not packages: utils.log.info('No MSL packages to uninstall') return # use the word REMOVE since it visibly looks different than UNINSTALL and INSTALL do utils._log_install_uninstall_message(packages, 'REMOVED') if not (yes or utils._ask_proceed()): return # After a MSL package gets uninstalled the "msl" namespace gets destroyed. # This is a known issue: # https://github.com/pypa/sample-namespace-packages/issues/5 # https://github.com/pypa/python-packaging-user-guide/issues/314 # There are few ways to bypass this issue # 1. force all MSL packages to require that the MSL Package Manager is installed # and only the MSL Package Manager contains the namespace __init__.py file # 2. modify the setup.py of each namespace package to have "packages=" include # only the child package. # 3. what is done below... assume that MSL packages are uninstalled using the # MSL Package Manager and then re-create the __init__.py files after a # package is uninstalled. def check_if_namespace_package(package_name): for dist in pkg_resources.working_set: if dist.project_name in package_name: split = package_name.split('-') if len(split) != 2: break examples_init_file = os.path.join(dist.module_path, split[0], 'examples', '__init__.py') if not os.path.isfile(examples_init_file): break with open(examples_init_file, mode='rt') as fp: examples_init = fp.readlines() init_file = os.path.join(dist.module_path, split[0], '__init__.py') if not os.path.isfile(init_file): break with open(init_file, mode='rt') as fp: init = fp.readlines() for line in init: if '__path__' in line and 'pkgutil' in line: return True, os.path.dirname(init_file), init, examples_init return False, None, None, None utils.log.info('') exe = [sys.executable, '-m', 'pip', 'uninstall'] if '--quiet' not in pip_options or '-q' not in pip_options: pip_options.extend(['--quiet'] * utils._pip_quiet) if '--disable-pip-version-check' not in pip_options: pip_options.append('--disable-pip-version-check') if '--yes' not in pip_options or '-y' not in pip_options: pip_options.append('--yes') for pkg in packages: is_namespace, path, init, examples_init = check_if_namespace_package(pkg) subprocess.call(exe + pip_options + [pkg]) if is_namespace and os.path.isdir(path): with open(os.path.join(path, '__init__.py'), mode='wt') as fp: fp.writelines(init) with open(os.path.join(path, 'examples', '__init__.py'), mode='wt') as fp: fp.writelines(examples_init)