Пример #1
0
    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
Пример #2
0
    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
Пример #3
0
    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