Source code for pyrosetta_help.installer

__doc__ = """
This module is aimed at helping to install pyrosetta.
If PyRosetta is installed but if it segfaults on import due to wrong GNU lib C (``glic``)
or there's a missing dependency that is a different matter.
"""

import argparse
import subprocess
import pip
from pip._internal.cli.main import main as pip_main
import importlib
import importlib.util
import os
import re
import requests
import site
import sys
from warnings import warn
from typing import (Optional)
from ._aux import *
from unittest.mock import Mock

[docs]def check_pyrosetta() -> bool: """ this will return True if * it is installed regardless if it segfaults or there's a missing dependency * something called pyrosetta has been loaded, including a Mock """ if 'pyrosetta' in sys.modules and isinstance(sys.modules['pyrosetta'], Mock): warn('PyRosetta is imported as a Mock module. This is not a problem for annotations etc, but it will not work.') return True elif 'pyrosetta' in sys.modules: return True else: return importlib.util.find_spec('pyrosetta') is not None
[docs]def download_pyrosetta(username: Optional[str] = None, password: Optional[str] = None, path: str = '.', hash_comparison_required: bool = True, on_preexisting:str='return') -> str: """ ``path`` is wither it is saved. ``on_preexisting`` can be 'return', 'raise', 'delete' or 'ignore' (& download anyway). """ existing_path = get_release_path(path) if existing_path is None: return existing_path elif on_preexisting == 'ignore': pass elif on_preexisting == 'delete': os.rmdir(existing_path) else: # error or raise raise FileExistsError(f'There seems to be a release already downloaded ({existing_path})') # get url and download: latest_url = get_latest_release_url(username, password, wheel=True, hash_comparison_required=hash_comparison_required) print(f'downloading: {latest_url}', flush=True) process: subprocess.CompletedProcess = subprocess.run(f'curl {latest_url} | tar -xj -C {path}'.split(), capture_output=True) assert not process.returncode, f'PyRosetta download failed (via {latest_url}, error: {process.stderr.decode()}. ' +\ 'Please check your internet connection and try again.' pyrosetta_folder = re.sub(r'.tar.\w+$', '', os.path.split(latest_url)[1]) return os.path.join(path, pyrosetta_folder)
[docs]def install_pyrosetta(username: Optional[str] = None, password: Optional[str] = None, path: str = '.', hash_comparison_required: bool = True) -> int: """ If there's a folder in ``path`` with PyRosetta release it will install that. If username and password arent provided the evironmental variables ``PYROSETTA_USERNAME`` or ``PYROSETTA_PASSWORD`` are used. It checks to see if hashes of the password is correct or the Rosetta one. """ if check_pyrosetta(): print('PyRosetta is already installed.') # ## local version present? path = get_release_path(path) if path: print(f'installing from local copy: {path}', flush=True) sig = pip_main(['install', path]) # process: subprocess.CompletedProcess = subprocess.run(f'yes | pip3 install -e {path}/setup/'.split(), # capture_output=True) # assert not process.returncode, f'PyRosetta installation failed ({path}, error: {process.stderr.decode()}.' # print('PyRosetta installed.') site.main() # refresh return sig # ## install by download # get latest release latest_url = get_latest_release_url(username, password, wheel=True, hash_comparison_required=hash_comparison_required) print(f'installing from remote: {latest_url}', flush=True) # this raises an OOM error # process: subprocess.CompletedProcess = subprocess.run(f'yes | pip install {latest_url}'.split(), # capture_output=True) # assert not process.returncode, f'PyRosetta installation failed (via {latest_url}, ' +\ # f'error: {process.stderr.decode()}. ' +\ # 'Please check your internet connection and try again.' # print('PyRosetta installed.') sig = pip_main(['install', latest_url]) site.main() # refresh return sig
[docs]def parse(): """ Parse command line arguments ``-h`` for help. :return: """ if check_pyrosetta(): print('PyRosetta is already installed.') SystemExit(0) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-u', '--username', type=str, help='PyRosetta username') parser.add_argument('-p', '--password', type=str, help='PyRosetta password') parser.add_argument('-f', '--file', type=str, help='Path where a release was release') args = parser.parse_args() install_pyrosetta(username=args.username, password=args.password, path=args.file, hash_comparison_required=True)
# ===================================================================================================================
[docs]def get_os_name() -> str: import platform if platform.system() == 'Windows': raise SystemError('Please Google what one does on Windows or install a real OS') elif platform.system() == 'Darwin' and platform.processor() in ('i386', 'x86_64'): return 'mac' elif platform.system() == 'Darwin' and platform.processor() == 'arm': return 'm1' elif platform.system() == 'Linux' and platform.processor() in ('i386', 'x86_64'): return 'ubuntu' # I could read /etc/os-release but I know pyrosetta works on CentOS. # Humbug is actually CentOS. elif platform.system() == 'Linux' and platform.machine() == 'armv7l': raise SystemError('You need to compile PyRosetta yourself for a Raspberry Pi') else: raise SystemError('Cannot detect OS type')
[docs]def get_latest_release_url(username: str, password: str, wheel: bool = True, hash_comparison_required: bool = True) -> str: """ Doing ``pip install xx:xxx@https://xx:xxx@graylab.jhu.edu/download/PyRos.../latest.html`` results in authetical loop drama in following the 302. """ username = parse_environmental(username, 'PYROSETTA_USERNAME') password = parse_environmental(password, 'PYROSETTA_PASSWORD') # check if hash is not the vanilla rosetta check_not_rosetta(username, password) if hash_comparison_required: check_correct(username, password) # get specifics: py_version = str(sys.version_info.major) + str(sys.version_info.minor) machine = get_os_name() # assemble os_specific = f'PyRosetta4.Release.python{py_version}.{machine}' if wheel: os_specific += '.wheel' for domain in ('graylab.jhu.edu/download/PyRosetta4/archive/release', 'west.rosettacommons.org/pyrosetta/release/release'): base_url = f'https://{username}:{password}@{domain}/{os_specific}' url_to_latest = f'{base_url}/latest.html' latest_response = requests.get(url_to_latest, # auth=requests.auth.HTTPBasicAuth(username, password) ) if latest_response.status_code == 401: raise ValueError('Incorrect username or password!') elif latest_response.status_code not in (200, 300, 301, 302, 303, 304, 305, 306, 307, 308): from IPython.display import display, HTML display(HTML(latest_response.text)) warn(f'Something is wrong with the url {url_to_latest}') continue return base_url + '/' + re.search(r'[uU][rR][lL]=(.*?)["\s]', latest_response.text).group(1) else: raise ValueError('Neither coast mirror works: '+ 'they are both down or '+ 'you are not connected to the internet or '+ 'the addresses have been changed.')
if __name__ == '__main__': parse()