"""
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)