Source code for pymyinstall.installhelper.install_cmd_helper

"""
Various function to install various python module from various location.


:githublink:`%|py|5`
"""
from __future__ import print_function

import sys
import platform
import os
import zipfile
import datetime
from .module_install_exceptions import UpdatePipError
from .run_cmd import run_cmd_private, run_cmd_old

if sys.version_info[0] == 2:
    FileNotFoundError = Exception


[docs]def python_version(): """ Retrieves the platform and version of this :epkg:`python`. :return: tuple, example: ("win32","32bit") or ("win32","64bit") :githublink:`%|py|24` """ return sys.platform, platform.architecture()[0]
[docs]def unzip_files(zipf, whereTo, fLOG=print): """ Unzip files from a :epkg:`zip` archive. :param zipf: archive :param whereTo: destination folder :param fLOG: logging function :return: list of unzipped files :githublink:`%|py|36` """ files = [] with zipfile.ZipFile(zipf, "r") as file: for info in file.infolist(): if not os.path.exists(info.filename): data = file.read(info.filename) tos = os.path.join(whereTo, info.filename) if not os.path.exists(tos): finalfolder = os.path.split(tos)[0] if not os.path.exists(finalfolder): fLOG(" creating folder ", finalfolder) os.makedirs(finalfolder) if not info.filename.endswith("/"): u = open(tos, "wb") u.write(data) u.close() files.append(tos) if fLOG: fLOG(" unzipped ", info.filename, " to ", tos) elif not tos.endswith("/"): files.append(tos) elif not info.filename.endswith("/"): files.append(info.filename) return files
[docs]def add_shortcut_to_desktop_for_module(name): """ Adds a shortcut on a module which includes a script. :param name: name of the module :return: shortcut was added or not :githublink:`%|py|68` """ if name == "spyder": from .link_shortcuts import add_shortcut_to_desktop, suffix from .module_install import ModuleInstall md = ModuleInstall("spyder", "exe", script="spyder.bat") sc = md.Script if os.path.exists(sc): ver = suffix() r = add_shortcut_to_desktop(sc, name + "." + ver, name + "." + ver) return os.path.exists(r) else: return False else: raise NotImplementedError( "nothing implemented for module: {0}".format(name))
[docs]def get_pip_program(exe=None): """ Gets :epkg:`pip` executable and fixes an issue with :epkg:`Pandoc`. :param exe: path to python executable :return: pip executable .. faqref:: :title: How can I check the dependencies? The module `pipdeptree <https://github.com/naiquevin/pipdeptree>`_ gives you something like:: d3py==0.2.3 - ipython [installed: 3.1.0] - networkx [installed: 1.9.1] - decorator [required: >=3.4.0, installed: 3.4.2] - numpy [installed: 1.9.2] - pandas [installed: 0.16.0] - pytz [required: >=2011k, installed: 2015.4] - python-dateutil [required: >=2, installed: 2.4.2] - six [required: >=1.5, installed: 1.9.0] - numpy [required: >=1.7.0, installed: 1.9.2] autopep8==1.1.1 - pep8 [required: >=1.5.7, installed: 1.5.7] sphinxjp.themes.basicstrap==0.4.2 - setuptools - Sphinx [installed: 1.3.1] - alabaster [required: >=0.7, installed: 0.7.4] - six [required: >=1.4, installed: 1.9.0] - colorama [installed: 0.3.3] - Pygments [required: >=2.0, installed: 2.0.2] - Babel [required: >=1.3, installed: 1.3] - pytz [required: >=0a, installed: 2015.4] - snowballstemmer [required: >=1.1, installed: 1.2.0] - docutils [required: >=0.11, installed: 0.12] - sphinx-rtd-theme [required: >=0.1, installed: 0.1.8] - Sphinx [required: >=1.3, installed: 1.3.1] - alabaster [required: >=0.7, installed: 0.7.4] - six [required: >=1.4, installed: 1.9.0] - colorama [installed: 0.3.3] - Pygments [required: >=2.0, installed: 2.0.2] - Babel [required: >=1.3, installed: 1.3] - pytz [required: >=0a, installed: 2015.4] - snowballstemmer [required: >=1.1, installed: 1.2.0] - docutils [required: >=0.11, installed: 0.12] - Jinja2 [required: >=2.3, installed: 2.7.3] - MarkupSafe [installed: 0.23] - Jinja2 [required: >=2.3, installed: 2.7.3] - MarkupSafe [installed: 0.23] ... :githublink:`%|py|138` """ tried = [] if exe is None: exe = os.path.dirname(sys.executable) major, minor = sys.version_info[0:2] if sys.platform.startswith("win"): if not exe.lower().endswith("scripts"): pi = os.path.join(exe, "Scripts", "pip.exe") tried.append(pi) if not os.path.exists(pi): pi = os.path.join(exe, "Scripts", "pip%d.exe" % major) tried.append(pi) if not os.path.exists(pi): # Anaconda is different pi = os.path.join(exe, "Scripts", "pip.exe") tried.append(pi) if not os.path.exists(pi): pi = os.path.join(exe, "Scripts", "pip%d.exe" % major) tried.append(pi) if not os.path.exists(pi): pi = os.path.join( exe, "Scripts", "pip%d.%d.exe" % (major, minor)) tried.append(pi) raise FileNotFoundError( "tried (1):\n" + "\n".join(tried) + "\n---- try ---\npython -m pip install -U pip --force") else: pi = os.path.join(exe, "pip.exe") tried.append(pi) if not os.path.exists(pi): # Anaconda is different pi = os.path.join(exe, "pip.exe") tried.append(pi) if not os.path.exists(pi): pi = os.path.join(exe, "pip%d.exe" % major) tried.append(pi) if not os.path.exists(pi): pi = os.path.join(exe, "pip%d.%d.exe" % (major, minor)) tried.append(pi) if not os.path.exists(pi): raise FileNotFoundError( "tried (2):\n" + "\n".join(tried) + "\n---- try ---\npython -m pip install -U pip --force") else: if sys.version_info[0] == 2: if exe is None: return "pip" else: pi = os.path.join(exe, "pip") else: major = sys.version_info[0] minor = sys.version_info[1] if exe is None: return "pip%d.%d" % (major, minor) else: # this does not work because on Linux, the binary is installed on the local path # pip3.4 are not in the same place # pi = os.path.join(exe, "pip%d.%d" % (major, minor)) import pip exe = os.path.normpath(os.path.join(os.path.dirname( pip.__file__), "..", "..", "..", "..", "bin")) pi = os.path.join(exe, "pip%d.%d" % (major, minor)) if not os.path.exists(pi): pi = os.path.join(exe, "pip") if not os.path.exists(pi): raise FileNotFoundError( "unable to find pip: {0}\n__file__={1}\nexe={2}".format(pi, pip.__file__, exe)) return pi
[docs]def get_python_program(): """ Returns the executable for :epkg:`python`. .. versionadded:: 1.1 :githublink:`%|py|211` """ pip = get_pip_program() dirname = os.path.dirname(pip) exe = os.path.join( dirname, "python.exe" if sys.platform.startswith("win") else "python") if os.path.exists(exe): return exe exe = os.path.normpath(os.path.join( dirname, "..", "python.exe" if sys.platform.startswith("win") else "python")) if os.path.exists(exe): return exe raise FileNotFoundError(exe)
[docs]def get_conda_program(exe=None): """ Gets :epkg:`conda` executable and fixes an issue with :epkg:`Pandoc`. :param exe: path to python executable :return: conda executable :githublink:`%|py|232` """ tried = [] if exe is None: exe = os.path.dirname(sys.executable) if sys.platform.startswith("win"): if not exe.lower().endswith("scripts"): pi = os.path.join(exe, "Scripts", "conda.exe") tried.append(pi) if not os.path.exists(pi): # Anaconda is different pi = os.path.join(exe, "Scripts", "conda.exe") tried.append(pi) if not os.path.exists(pi): raise FileNotFoundError( "tried (1):\n" + "\n".join(tried)) else: pi = os.path.join(exe, "conda.exe") tried.append(pi) if not os.path.exists(pi): # Anaconda is different pi = os.path.join(exe, "conda.exe") tried.append(pi) if not os.path.exists(pi): raise FileNotFoundError( "tried (2):\n" + "\n".join(tried)) else: if exe is None: return "conda" else: pi = os.path.join(exe, "conda") return pi
[docs]def get_file_modification_date(filename): """ Gets the date modification for a filename. :param filename: filename :return: datetime :githublink:`%|py|272` """ t = os.path.getmtime(filename) return datetime.datetime.fromtimestamp(t)
[docs]def update_pip(python_path=None, fLOG=print): """ Updates :epkg:`pip` for a specific distribution. :param python_path: python path (or sys.executable if None) :param fLOG: logging function :return: output The command ``python -m pip install -U pip`` or ``pip install --upgrade pip`` might fail on Windows due to very long paths (see `Upgrading pip fails on Windows when install path is too long <https://github.com/pypa/pip/issues/3055>`_). If that happens, assuming the module *pymyinstall* was installed with pip, we can now remove *pip* and use *get_pip.py* instead. This part requires *pyquickhelper*. We try the url `bootstrap.pypa.io/get-pip.py <https://bootstrap.pypa.io/get-pip.py>`_ first then a local copy. :githublink:`%|py|294` """ if python_path is None: python_path = sys.executable else: python_path = os.path.join(python_path, "python") cmd = python_path + " -m pip install -U pip" out, err = run_cmd(cmd, wait=True, fLOG=fLOG) if err and len(err) > 0: if ("FileNotFoundError" in err or "No module named pip.__main__" in err) \ and sys.platform.startswith("win"): from pyquickhelper.filehelper import remove_folder # we try to remove pip and to install it again # it might be due to long path on Windows pack = os.path.join(os.path.dirname( python_path), "Lib", "site-packages") if not os.path.exists(pack): raise FileNotFoundError(pack) fpip = os.path.join(pack, "pip") if os.path.exists(fpip): # remove the folder fLOG(" remove folder", fpip) remove_folder(fpip) pip_ = [_ for _ in os.listdir(pack) if _.startswith("pip-")] if len(pip_) > 0: for _ in pip_: fp = os.path.join(pack, _) fLOG(" remove folder", fp) remove_folder(fpip) url = "https://bootstrap.pypa.io/get-pip.py" cmd = python_path + " " + url out, err = run_cmd(cmd, wait=True) if err and len(err) > 0: get_pip = os.path.abspath(os.path.join( os.path.dirname(__file__), "get_pip.py")) if not os.path.exists(get_pip): raise FileNotFoundError(get_pip) cmd = python_path + " " + get_pip out, err = run_cmd(cmd, wait=True) if err and len(err) > 0: raise UpdatePipError( "unable to update pip with get_pip.\nCMD:\n{0}\nOUT:\n{1}\nERR-E:\n{2}".format(cmd, out, err)) else: lines = err.split("\n") keep = [] for line in lines: if len(line.strip("\n\r\t ")) == 0: continue if "Prompt dismissed.." in line: continue if not line.startswith(" ") and "RuntimeWarning: Config variable" not in line and \ not(" which is incompatible." in line and " has requirement " in line) and \ not(" requires " in line and " which is not installed." in line) and \ not("Cache entry deserialization failed, entry ignored" in line) and \ len(line.strip()) > 3: keep.append(line) if len(keep) > 0 and "Requirement already up-to-date" not in out: for _ in keep: print("++", _) raise UpdatePipError( "Unable to update pip.\nCMD:\n{0}\nOUT:\n{1}\nERR-F:" "\n{2}\n---KEPT---\n{3}".format(cmd, out, err, "\n".join(keep))) return out
[docs]def has_pip(): """ Tells if :epkg:`pip` is installed. :return: boolean :githublink:`%|py|365` """ try: import pip return pip is not None except ImportError: return False
[docs]def is_conda_distribution(): """ Tells if it is a :epkg:`conda` distribution or not, check the presence of ``Continuum Analytics`` or ``|Anaconda`` in ``sys.version``. :return: boolean .. versionadded:: 1.1 :githublink:`%|py|382` """ return "Continuum Analytics" in sys.version or "|Anaconda" in sys.version
[docs]def run_cmd(cmd, sin="", shell=sys.platform.startswith("win"), wait=False, log_error=True, stop_running_if=None, encerror="ignore", encoding="utf8", change_path=None, communicate=True, preprocess=True, timeout=None, catch_exit=False, fLOG=None, tell_if_no_output=None, old_behavior=False): """ Runs a command line and waits for the results, :func:`run_cmd_private <pymyinstall.installhelper.run_cmd.run_cmd_private>`. :githublink:`%|py|394` """ if old_behavior or not sys.platform.startswith("win"): return run_cmd_old( cmd=cmd, sin=sin, shell=shell, wait=wait, log_error=log_error, secure=None, stop_waiting_if=stop_running_if, do_not_log=False, encerror=encerror, encoding=encoding, cwd=change_path, fLOG=fLOG) return run_cmd_private( cmd=cmd, sin=sin, shell=shell, wait=wait, log_error=log_error, stop_running_if=stop_running_if, encerror=encerror, encoding=encoding, change_path=change_path, communicate=communicate, preprocess=preprocess, timeout=timeout, catch_exit=catch_exit, fLOG=fLOG, tell_if_no_output=tell_if_no_output)