def _install(self, name, version, repos): '''Check existence and version match of R library. cran and bioc packages are unique yet might overlap with github. Therefore if the input name is {repo}/{pkg} the package will be installed from github if not available, else from cran or bioc ''' from sos.pattern import glob_wildcards import tempfile import subprocess output_file = tempfile.NamedTemporaryFile(mode='w+t', suffix='.txt', delete=False).name script_file = tempfile.NamedTemporaryFile(mode='w+t', suffix='.R', delete=False).name if len(glob_wildcards('{repo}@{pkg}', [name])['repo']): # package is from github self._install('devtools', None, repos) install_script = f''' options(warn=-1) package_repo <-strsplit("{name}", split="@")[[1]][2] package <-strsplit("{name}", split="@")[[1]][1] if (suppressMessages(require(package, character.only=TRUE, quietly=TRUE))) {{ write(paste(package, packageVersion(package), "AVAILABLE"), file="{output_file}") }} else {{ devtools::install_github(package_repo) # if it still does not exist, write the package name to output if (suppressMessages(require(package, character.only=TRUE, quietly=TRUE))) {{ write(paste(package, packageVersion(package), "INSTALLED"), file="{output_file}") }} else {{ write(paste(package, "NA", "MISSING"), file="{output_file}") quit("no") }} }} cur_version <- packageVersion(package) ''' else: # package is from cran or bioc install_script = f''' options(warn=-1) package <- "{name}" if (suppressMessages(require(package, character.only=TRUE, quietly=TRUE))) {{ write(paste(package, packageVersion(package), "AVAILABLE"), file={output_file!r}) }} else {{ install.packages(package, repos="{repos}", quiet=FALSE) # if the package still does not exist if (!suppressMessages(require(package, character.only=TRUE, quietly=TRUE))) {{ source("http://bioconductor.org/biocLite.R") biocLite(package, suppressUpdates=TRUE, suppressAutoUpdate=TRUE, ask=FALSE) }} # if it still does not exist, write the package name to output if (suppressMessages(require(package, character.only=TRUE, quietly=TRUE))) {{ write(paste(package, packageVersion(package), "INSTALLED"), file={output_file!r}) }} else {{ write(paste(package, "NA", "MISSING"), file={output_file!r}) quit("no") }} }} cur_version <- packageVersion(package) ''' version_script = '' if version is not None: version = list(version) operators = [] for idx, value in enumerate(version): value = str(value) if value.endswith('+'): operators.append('>=') version[idx] = value[:-1] elif value.endswith('-'): operators.append('<') version[idx] = value[:-1] else: operators.append('==') # check version and mark version mismatch # if current version satisfies any of the # requirement the check program quits for x, y in zip(version, operators): version_script += f''' if (cur_version {y} {repr(x)}) {{ quit("no") }} ''' version_script += f'write(paste(package, cur_version, "VERSION_MISMATCH"), file = {repr(output_file)})' # temporarily change the run mode to run to execute script try: with open(script_file, 'w') as sfile: sfile.write(install_script + version_script) # p = subprocess.Popen( ['Rscript', '--default-packages=utils', script_file]) ret = p.wait() if ret != 0: env.logger.warning('Failed to detect or install R library') return False except Exception as e: env.logger.error(f'Failed to execute script: {e}') return False finally: os.remove(script_file) ret_val = False with open(output_file) as tmp: for line in tmp: lib, version, status = line.split() if status.strip() == "MISSING": env.logger.warning( f'R Library {lib} is not available and cannot be installed.' ) elif status.strip() == 'AVAILABLE': env.logger.debug( f'R library {lib} ({version}) is available') ret_val = True elif status.strip() == 'INSTALLED': env.logger.debug( f'R library {lib} ({version}) has been installed') ret_val = True elif status.strip() == 'VERSION_MISMATCH': env.logger.warning( f'R library {lib} ({version}) does not satisfy version requirement!' ) else: raise RuntimeError(f'This should not happen: {line}') try: os.remove(output_file) except Exception: pass return ret_val
def _install(self, name, version, repos): """Check existence and version match of R library. cran and bioc packages are unique yet might overlap with github. Therefore if the input name is {repo}/{pkg} the package will be installed from github if not available, else from cran or bioc """ from sos.pattern import glob_wildcards import tempfile import subprocess output_file = tempfile.NamedTemporaryFile(mode="w+t", suffix=".txt", delete=False).name script_file = tempfile.NamedTemporaryFile(mode="w+t", suffix=".R", delete=False).name # package_loaded = ( "suppressMessages(require(package, character.only=TRUE, quietly=TRUE))" ) version_satisfied = "TRUE" for opt in ("==", ">=", ">", "<=", "<", "!="): if opt in name: if version is not None: raise ValueError( f"Specifying 'version=' option in addition to '{name}' is not allowed" ) name, version = [x.strip() for x in name.split(opt, 1)] if "," in version: raise ValueError( f"SoS does not yet support multiple version comparisons. {version} provided" ) version = (opt + version, ) break if version is not None: version = list(version) operators = [] for idx, value in enumerate(version): value = str(value) if value.startswith(">="): operators.append(">=") version[idx] = value[2:] elif value.startswith(">"): operators.append(">") version[idx] = value[1:] elif value.startswith("<="): operators.append("<=") version[idx] = value[2:] elif value.startswith("<"): operators.append("<") version[idx] = value[1:] elif value.startswith("=="): operators.append("==") version[idx] = value[2:] elif value.startswith("!="): operators.append("!=") version[idx] = value[2:] else: operators.append("==") # check version and mark version mismatch # if current version satisfies any of the # requirement the check program quits version_satisfied = "||".join([ f"(cur_version {y} {repr(x)})" for x, y in zip(version, operators) ]) # if len(glob_wildcards("{repo}@{pkg}", [name])["repo"]): # package is from github self._install("remotes>=2.0.0", None, repos) install_script = f""" options(warn=-1) package_repo <-strsplit("{name}", split="@")[[1]][2] package <-strsplit("{name}", split="@")[[1]][1] if ({package_loaded}) cur_version <- packageVersion(package) else cur_version <- NULL if (!is.null(cur_version) && {version_satisfied}) {{ write(paste(package, cur_version, "AVAILABLE"), file={repr(output_file)}) }} else if ({"TRUE" if self._autoinstall else "FALSE"}) {{ remotes::install_github(package_repo, force=TRUE, upgrade="never") if ({package_loaded}) cur_version <- packageVersion(package) else cur_version <- NULL # if it still does not exist, write the package name to output if (!is.null(cur_version)) {{ if ({version_satisfied}) write(paste(package, cur_version, "INSTALLED"), file={repr(output_file)}) else write(paste(package, cur_version, "VERSION_MISMATCH"), file={repr(output_file)}) }} else {{ write(paste(package, "NA", "MISSING"), file={repr(output_file)}) quit("no") }} }} else {{ if (!is.null(cur_version)) write(paste(package, cur_version, "VERSION_MISMATCH"), file={repr(output_file)}) else write(paste(package, cur_version, "UNAVAILABLE"), file={repr(output_file)}) }} """ else: # package is from cran or bioc install_script = f""" options(warn=-1) package <- "{name}" if ({package_loaded}) cur_version <- packageVersion(package) else cur_version <- NULL if (!is.null(cur_version) && {version_satisfied}) {{ write(paste(package, cur_version, "AVAILABLE"), file={repr(output_file)}) }} else if ({"TRUE" if self._autoinstall else "FALSE"}) {{ install.packages(package, repos="{repos}", quiet=FALSE) # if the package still does not exist if (!{package_loaded}) {{ source("http://bioconductor.org/biocLite.R") biocLite(package, suppressUpdates=TRUE, suppressAutoUpdate=TRUE, ask=FALSE) }} if ({package_loaded}) cur_version <- packageVersion(package) else cur_version <- NULL # if it still does not exist, write the package name to output if (!is.null(cur_version)) {{ if ({version_satisfied}) write(paste(package, cur_version, "INSTALLED"), file={repr(output_file)}) else write(paste(package, cur_version, "VERSION_MISMATCH"), file={repr(output_file)}) }} else {{ write(paste(package, "NA", "MISSING"), file={repr(output_file)}) quit("no") }} }} else {{ if (!is.null(cur_version)) write(paste(package, cur_version, "VERSION_MISMATCH"), file={repr(output_file)}) else write(paste(package, cur_version, "UNAVAILABLE"), file={repr(output_file)}) }} """ # temporarily change the run mode to run to execute script try: with open(script_file, "w") as sfile: sfile.write(install_script) # p = subprocess.Popen( ["Rscript", "--default-packages=utils", script_file]) ret = p.wait() if ret != 0: env.logger.warning( f"Failed to detect or install R library {name}") return False except Exception as e: env.logger.error(f"Failed to execute script: {e}") return False finally: os.remove(script_file) ret_val = False with open(output_file) as tmp: for line in tmp: lib, cur_version, status = line.split(" ", 2) if status.strip() == "MISSING": env.logger.error( f"R library {lib} is not available and cannot be installed." ) elif status.strip() == "UNAVAILABLE": env.logger.error(f"R library {lib} is not available.") elif status.strip() == "AVAILABLE": env.logger.debug( f"R library {lib} ({cur_version}) is available") ret_val = True elif status.strip() == "INSTALLED": env.logger.debug( f"R library {lib} ({cur_version}) has been installed") ret_val = True elif status.strip() == "VERSION_MISMATCH": env.logger.error( f'R library {lib} ({cur_version}) does not satisfy version requirement ({"/".join(version)})!' ) else: raise RuntimeError(f"This should not happen: {line}") try: os.remove(output_file) except Exception: pass return ret_val
def _install(self, name, version, repos): '''Check existence and version match of R library. cran and bioc packages are unique yet might overlap with github. Therefore if the input name is {repo}/{pkg} the package will be installed from github if not available, else from cran or bioc ''' from sos.pattern import glob_wildcards from sos.sos_eval import interpolate import tempfile import shlex import subprocess output_file = tempfile.NamedTemporaryFile(mode='w+t', suffix='.txt', delete=False).name script_file = tempfile.NamedTemporaryFile(mode='w+t', suffix='.R', delete=False).name if len(glob_wildcards('{repo}/{pkg}', [name])['repo']): # package is from github self._intall('devtools', version, repos) install_script = interpolate(''' options(warn=-1) package_repo <- ${name!r} package <- basename(package_repo) if (require(package, character.only=TRUE, quietly=TRUE)) { write(paste(package, packageVersion(package), "AVAILABLE"), file="${output_file}") } else { devtools::install_github(package_repo) # if it still does not exist, write the package name to output if (require(package, character.only=TRUE, quietly=TRUE)) { write(paste(package, packageVersion(package), "INSTALLED"), file="${output_file}") } else { write(paste(package, "NA", "MISSING"), file="${output_file}") quit("no") } } cur_version <- packageVersion(package) ''', '${ }', locals()) else: # package is from cran or bioc install_script = interpolate(''' options(warn=-1) package <- ${name!r} if (require(package, character.only=TRUE, quietly=TRUE)) { write(paste(package, packageVersion(package), "AVAILABLE"), file="${output_file}") } else { install.packages(package, repos="${repos}", quiet=FALSE) # if the package still does not exist if (!require(package, character.only=TRUE, quietly=TRUE)) { source("http://bioconductor.org/biocLite.R") biocLite(package, suppressUpdates=TRUE, suppressAutoUpdate=TRUE, ask=FALSE) } # if it still does not exist, write the package name to output if (require(package, character.only=TRUE, quietly=TRUE)) { write(paste(package, packageVersion(package), "INSTALLED"), file="${output_file}") } else { write(paste(package, "NA", "MISSING"), file="${output_file}") quit("no") } } cur_version <- packageVersion(package) ''', '${ }', locals()) version_script = '' if version is not None: version = [version] if isinstance(version, str) else version operators = [] for idx, value in enumerate(version): value = str(value) if value.endswith('+'): operators.append('>=') version[idx] = value[:-1] elif value.endswith('-'): operators.append('<') version[idx] = value[:-1] else: operators.append('==') # check version and mark version mismatch # if current version satisfies any of the # requirement the check program quits for x, y in zip(version, operators): version_script += ''' if (cur_version {1} {0}) {{ quit("no") }} '''.format(repr(x), y) version_script += 'write(paste(package, cur_version, "VERSION_MISMATCH"), file = {})'.\ format(repr(output_file)) # temporarily change the run mode to run to execute script try: with open(script_file, 'w') as sfile: sfile.write(install_script + version_script) cmd = 'Rscript --default-packages=utils ' + shlex.quote(script_file) # p = subprocess.Popen(cmd, shell=True) ret = p.wait() if ret != 0: env.logger.warning('Failed to detect or install R library') return False except Exception as e: env.logger.error('Failed to execute script: {}'.format(e)) return False finally: os.remove(script_file) ret_val = False with open(output_file) as tmp: for line in tmp: lib, version, status = line.split() if status.strip() == "MISSING": env.logger.warning('R Library {} is not available and cannot be installed.'.format(lib)) elif status.strip() == 'AVAILABLE': env.logger.debug('R library {} ({}) is available'.format(lib, version)) ret_val = True elif status.strip() == 'INSTALLED': env.logger.debug('R library {} ({}) has been installed'.format(lib, version)) ret_val = True elif status.strip() == 'VERSION_MISMATCH': env.logger.warning('R library {} ({}) does not satisfy version requirement!'.format(lib, version)) else: raise RuntimeError('This should not happen: {}'.format(line)) try: os.remove(output_file) except: pass return ret_val