예제 #1
0
class NpmRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``npm`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('npm')}

    def __init__(self, package, version=""):
        """
        Constructs a new ``NpmRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = NpmRequirement('ramllint', '6.2')
        >>> pr.type
        'npm'
        >>> pr.package
        'ramllint'
        >>> pr.version
        '6.2'
        >>> str(pr)
        'ramllint 6.2'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'npm', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> NpmRequirement('alex', '2').install_command()
        ['npm', 'install', 'alex@2']

        >>> NpmRequirement('alex').install_command()
        ['npm', 'install', 'alex']

        :param return: A string with the installation command.
        """
        result = [
            'npm', 'install',
            self.package + "@" + self.version if self.version else self.package
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        for cmd in ('npm list ' + self.package, 'npm list -g ' + self.package):

            if not run(cmd, stdout=Capture(), stderr=Capture()).returncode:
                return True

        return False
예제 #2
0
class LuarocksRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``luarocks`` packages automatically and provide a function to
    check for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('luarocks')}

    def __init__(self, package, version=''):
        """
        Constructs a new ``Luarocks``, using the ``PackageRequirement``
        constructor.

        >>> pr = LuarocksRequirement('luasocket', '3.0rc1')
        >>> pr.type
        'luarocks'
        >>> pr.package
        'luasocket'
        >>> pr.version
        '3.0rc1'
        >>> str(pr)
        'luasocket 3.0rc1'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'luarocks', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> LuarocksRequirement('luacheck', '0.19.1').install_command()
        ['luarocks', 'install', 'luacheck 0.19.1']

        >>> LuarocksRequirement('luacheck').install_command()
        ['luarocks', 'install', 'luacheck']

        :param return: A string with the installation command.
        """
        result = [
            'luarocks', 'install',
            self.package + ' ' + self.version if self.version else self.package
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        cmd = 'luarocks show ' + self.package

        if not run(cmd, stdout=Capture(), stderr=Capture()).returncode:
            return True

        return False
예제 #3
0
class JuliaRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``julia`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('julia')}

    def __init__(self, package, version=""):
        """
        Constructs a new ``JuliaRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = JuliaRequirement('Lint', '19.2')
        >>> pr.type
        'julia'
        >>> pr.package
        'Lint'
        >>> pr.version
        '19.2'
        >>> str(pr)
        'Lint 19.2'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'julia', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> JuliaRequirement('Lint').install_command()
        'julia -e \\'Pkg.add("Lint")\\''

        :return: A string with the installation command.
        """
        code = 'Pkg.add("{}")'.format(escape(self.package, '\\"'))
        args = ('julia', '-e', shlex.quote(code))
        return ' '.join(args)

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :return: ``True`` if dependency is installed, ``False`` otherwise.
        """
        # We need to check explicitly if `nothing` is returned, as this happens
        # when the package is *registered*, but *not installed*. If it's not
        # even registered, julia will throw an exception which lets julia exit
        # with an error code different from 0.
        code = 'Pkg.installed("{}")==nothing?exit(1):exit(0)'.format(
            escape(self.package, '\\"'))
        args = 'julia -e ' + shlex.quote(code)
        return not run(args, stdout=Capture(), stderr=Capture()).returncode
예제 #4
0
class CabalRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``, It specifies the proper
    type for ``cabal`` packages automatically and provides a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('cabal')}

    def __init__(self, package, version=''):
        """
        Constructs a new ``CabalRequirement``, using the ``PackageRequirement``
        constructor.

        >>> cr = CabalRequirement('zoom', '0.1')
        >>> cr.type
        'cabal'
        >>> cr.package
        'zoom'
        >>> cr.version
        '0.1'
        >>> str(cr)
        'zoom 0.1'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'cabal', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> CabalRequirement('foo', '3').install_command()
        ['cabal', 'install', 'foo-3']
        >>> CabalRequirement('foo').install_command()
        ['cabal', 'install', 'foo']

        :param return: A list with the installation command parameters.
        """
        result = [
            'cabal', 'install',
            self.package + '-' + self.version if self.version else self.package
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        return not run('cabal info ' + self.package,
                       stdout=Capture(),
                       stderr=Capture()).returncode
예제 #5
0
class GoRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``go`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('go')}

    def __init__(self, package, version="", flag=""):
        """
        Constructs a new ``GoRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = GoRequirement('github.com/golang/lint/golint', '19.2', '-u')
        >>> pr.type
        'go'
        >>> pr.package
        'github.com/golang/lint/golint'
        >>> pr.version
        '19.2'
        >>> pr.flag
        '-u'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        :param flag:    A string that specifies any additional flags, that
                        are passed to the manager.
        """
        PackageRequirement.__init__(self, 'go', package, version)
        self.flag = flag

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> GoRequirement(
        ...     'github.com/golang/lint/golint', '' , '-u' ).install_command()
        ['go', 'get', '-u', 'github.com/golang/lint/golint']

        :param return: A string with the installation command.
        """
        return ['go', 'get', self.flag, self.package]

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        return not run('go list ' + self.package,
                       stdout=Capture(),
                       stderr=Capture()).returncode
예제 #6
0
def get_all_requirements(bears):
    bear_requirements = {}

    for bear in bears:
        instance_dict = collections.defaultdict(set)
        executable = None
        if hasattr(bear, 'get_executable'):
            executable = bear.get_executable()
        if executable:
            requirement = ExecutableRequirement(_clean_executable(executable))
            instance_dict['ExecutableRequirement'].add(requirement)
        helper(bear.REQUIREMENTS, instance_dict)
        bear_requirements[str(bear.name)] = instance_dict

    return bear_requirements
예제 #7
0
class GemRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``ruby`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('gem')}

    def __init__(self, package, version="", require=""):
        """
        Constructs a new ``GemRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = GemRequirement('setuptools', '19.2', 'flag')
        >>> pr.type
        'gem'
        >>> pr.package
        'setuptools'
        >>> pr.version
        '19.2'
        >>> pr.require
        'flag'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        :param require: A string that specifies any additional flags, that
                        would be used with ``require``.
        """
        PackageRequirement.__init__(self, 'gem', package, version)
        self.require = require

    def install_command(self):
        """
        Creates the installation command for the instance of the class.
        >>> GemRequirement('rubocop').install_command()
        ['gem', 'install', 'rubocop']

        >>> GemRequirement('scss_lint', '', 'false').install_command()
        ['gem', 'install', 'scss_lint']

        >>> GemRequirement('rake', '10.5.0').install_command()
        ['gem', 'install', 'rake:10.5.0']

        :param return: A string with the installation command.
        """

        gem = self.package
        if self.version:
            gem += ':' + self.version
        result = ['gem', 'install', gem]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        cmd = 'gem list -i ' + self.package

        return not run(cmd, stdout=Capture(), stderr=Capture()).returncode
예제 #8
0
class RscriptRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``R`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('R')}

    def __init__(self,
                 package,
                 version="",
                 flag="",
                 repo="http://cran.rstudio.com"):
        """
        Constructs a new ``RscriptRequirement``, using the
        ``PackageRequirement`` constructor.

        >>> pr = RscriptRequirement(
        ...         'formatR', version='1.4', flag='-e',
        ...         repo="http://cran.rstudio.com")
        >>> pr.type
        'R'
        >>> pr.package
        'formatR'
        >>> pr.version
        '1.4'
        >>> str(pr)
        'formatR 1.4'
        >>> pr.flag
        '-e'
        >>> pr.repo
        'http://cran.rstudio.com'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        :param flag:    A string that specifies any additional flags, that
                        are passed to the type.
        :param repo:    The repository from which the package is to be
                        installed.
        """
        PackageRequirement.__init__(self, 'R', package, version, repo)
        self.flag = flag

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> RscriptRequirement(
        ...     'formatR', '' , '-e',
        ...     'http://cran.rstudio.com').install_command()
        ['R', '-e', '"install.packages("formatR", repo="http://cran.rstudio.com", dependencies=TRUE)"']

        :param return: A list with the installation command.
        """
        install = '"install.packages(\"{}\", repo=\"{}\", dependencies=TRUE)"'
        result = ['R', '-e', install.format(self.package, self.repo)]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        return not run(
            ('R -e \'library(\"{}\", quietly=TRUE)\''.format(self.package)),
            stdout=Capture(),
            stderr=Capture()).returncode
예제 #9
0
class CondaRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``conda`` packages automatically and provides a function to check
    for the requirement.
    """

    REQUIREMENTS = {
        ExecutableRequirement('conda'),
        ExecutableRequirement('grep')
    }

    def __init__(self, package, version='', repo=''):
        """
        Constructs a new ``CondaRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = CondaRequirement('scipy', '0.15.0')
        >>> pr.type
        'conda'
        >>> pr.package
        'scipy'
        >>> pr.version
        '0.15.0'
        >>> str(pr)
        'scipy 0.15.0'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        :param repo: The repository from which the package is to be installed.
        """
        PackageRequirement.__init__(self, 'conda', package, version, repo)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> CondaRequirement('scipy', '0.15.0').install_command()
        ['conda', 'install', '--yes', 'scipy=0.15.0']

        >>> CondaRequirement('scipy').install_command()
        ['conda', 'install', '--yes', 'scipy']

        >>> CondaRequirement('bottleneck', '0.8.0', 'pandas').install_command()
        ['conda', 'install', '--yes', '-c', 'pandas', 'bottleneck=0.8.0']

        :param return: A string with the installation command.
        """
        conda_install = ['conda', 'install', '--yes']
        conda_package_version = [
            self.package + '=' + self.version if self.version else self.package
        ]
        if self.repo:
            result = conda_install + ['-c', self.repo] + conda_package_version
        else:
            result = conda_install + conda_package_version
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        cmd = 'conda list {} | grep "^{}"'

        if not run(cmd.format(self.package, self.package),
                   stdout=Capture(),
                   stderr=Capture()).returncode:
            return True

        return False
예제 #10
0
class CPDBear(GlobalBear):

    language_dict = {
        'C#': 'cs',
        'CPP': 'cpp',
        'JavaScript': 'ecmascript',
        'Fortran': 'fortran',
        'Go': 'go',
        'Java': 'java',
        'JSP': 'jsp',
        'Matlab': 'matlab',
        'Octave': 'matlab',
        'Objective-C': 'objectivec',
        'PHP': 'php',
        'PLSQL': 'plsql',
        'Python': 'python',
        'Python 2': 'python',
        'Python 3': 'python',
        'Ruby': 'ruby',
        'Scala': 'scala',
        'Swift': 'swift'
    }

    LANGUAGES = set(language_dict.keys())
    REQUIREMENTS = {
        AnyOneOfRequirements([
            ExecutableRequirement('cpd'),
            ExecutableRequirement('run.sh'),
        ]),
    }
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Duplication'}

    def run(
        self,
        language: language,
        minimum_tokens: int = 20,
        ignore_annotations: bool = False,
        ignore_identifiers: bool = True,
        ignore_literals: bool = False,
        ignore_usings: bool = False,
        skip_duplicate_files: bool = True,
    ):
        """
        Checks for similar code that looks as it could be replaced to reduce
        redundancy.

        For more details see:
        <https://pmd.github.io/pmd-6.4.0/pmd_userdocs_cpd.html>

        :param language:
            One of the supported languages of this bear.
        :param minimum_tokens:
            The minimum token length which should be reported as a duplicate.
        :param ignore_annotations:
            Ignore language annotations when comparing text.
        :param ignore_identifiers:
            Ignore constant and variable names when comparing text.
        :param ignore_literals:
            Ignore number values and string contents when comparing text.
        :param ignore_usings:
            Ignore ``using`` directives in C#.
        :param skip_duplicate_files:
            Ignore multiple copies of files of the same name and length in
            comparison.
        """
        for supported_lang in self.language_dict:
            if supported_lang in language:
                cpd_language = self.language_dict[supported_lang]
                break
        else:
            self.err('This bear does not support files with the extension '
                     "'{}'.".format(language))
            return

        options = {
            '--ignore-annotations': ignore_annotations,
            '--ignore-identifiers': ignore_identifiers,
            '--ignore-literals': ignore_literals,
            '--ignore-usings': ignore_usings,
            '--skip-duplicate-files': skip_duplicate_files
        }

        files = ','.join(self.file_dict.keys())
        executable = which('cpd') or which('run.sh')
        executable = tuple([executable] if not executable.endswith('run.sh')
                           else [executable, 'cpd'])

        arguments = ('--skip-lexical-errors', '--minimum-tokens',
                     str(minimum_tokens), '--language', cpd_language,
                     '--files', files, '--format', 'xml')

        arguments += tuple(option for option, enable in options.items()
                           if enable is True)

        arguments = executable + arguments
        stdout_output, _ = run_shell_command(arguments)

        if stdout_output:
            root = ElementTree.fromstring(stdout_output)

            for duplication in root.findall('duplication'):
                length = int(duplication.attrib['lines'])
                affected_code = list()

                for xml_file in duplication.findall('file'):
                    filename = xml_file.attrib['path']
                    start_line = int(xml_file.attrib['line'])
                    end_line = min(start_line + length - 1,
                                   len(self.file_dict[filename]))

                    affected_code.append(
                        SourceRange.from_values(filename,
                                                start_line=start_line,
                                                end_line=end_line))

                yield Result(
                    self,
                    'Duplicate code found.',
                    affected_code,
                    additional_info=(
                        'Duplicate code is an indicator '
                        'that you have more code than you need. Consider'
                        ' refactor your code to remove one of the'
                        ' occurrences. For more information go here:'
                        'http://tinyurl.com/coala-clone'))
예제 #11
0
class CargoRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``cargo`` packages automatically and provides a function to
    check for the requirement.
    """

    REQUIREMENTS = {
        ExecutableRequirement('cargo'),
        ExecutableRequirement('grep'),
    }

    def __init__(self, package, version=''):
        """
        Constructs a new ``CargoRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = CargoRequirement('pulldown-cmark', '0.0.14')
        >>> pr.type
        'cargo'
        >>> pr.package
        'pulldown-cmark'
        >>> pr.version
        '0.0.14'
        >>> str(pr)
        'pulldown-cmark 0.0.14'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'cargo', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> CargoRequirement('pulldown-cmark', '0.0.14').install_command()
        ['cargo', 'install', '--vers 0.0.14 pulldown-cmark']

        >>> CargoRequirement('pulldown-cmark').install_command()
        ['cargo', 'install', 'pulldown-cmark']

        :param return: A string with the installation command.
        """
        result = [
            'cargo', 'install', '--vers ' + self.version + ' ' +
            self.package if self.version else self.package
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        cmd = 'cargo install --list | grep "^{}"'

        if not run(cmd.format(self.package),
                   stdout=Capture(),
                   stderr=Capture()).returncode:
            return True

        return False
예제 #12
0
class PipRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``python`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement(sys.executable)}

    def __init__(self, package, version=""):
        """
        Constructs a new ``PipRequirement``, using the ``PackageRequirement``
        constructor.

        >>> pr = PipRequirement('setuptools', '19.2')
        >>> pr.type
        'pip'
        >>> pr.package
        'setuptools'
        >>> pr.version
        '19.2'
        >>> str(pr)
        'setuptools 19.2'

        :param package: A string with the name of the package to be installed.
        :param version: A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'pip', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        :param return: A list with the installation command parameters.
        """
        result = [
            sys.executable, '-m', 'pip', 'install', self.package + '==' +
            self.version if self.version else self.package
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        return not run(sys.executable + ' -m pip show ' + self.package,
                       stdout=Capture(),
                       stderr=Capture()).returncode

    def upgrade_package(self):
        """
        Runs the upgrade command for the package given in a sub-process.
        """
        run(sys.executable + ' -m pip install ' + self.package + ' --upgrade',
            stdout=Capture(),
            stderr=Capture())

    def uninstall_package(self):
        """
        Runs the uninstall command for the package given in a sub-process.
        """
        run(sys.executable + ' -m pip uninstall -y ' + self.package,
            stdout=Capture(),
            stderr=Capture())
예제 #13
0
class ComposerRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the proper
    type for ``Composer`` packages automatically and provides a function to
    check for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('composer')}

    def __init__(self, package, version=""):
        """
        Constructs a new ``ComposerRequirement``, using the
        ``PackageRequirement`` constructor.

        >>> pr = ComposerRequirement('phpstan/phpstan', '0.6.4')
        >>> pr.type
        'composer'
        >>> pr.package
        'phpstan/phpstan'
        >>> pr.version
        '0.6.4'
        >>> str(pr)
        'phpstan/phpstan 0.6.4'

        :param package:
            A string with the name of the package to be installed.
        :param version:
            A version string. Leave empty to specify latest version.
        """
        PackageRequirement.__init__(self, 'composer', package, version)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> ComposerRequirement('phpstan/phpstan', '0.6.4').install_command()
        ['composer', 'require', 'phpstan/phpstan:0.6.4']

        >>> ComposerRequirement('phpstan/phpstan').install_command()
        ['composer', 'require', 'phpstan/phpstan']

        :param return: A string with the installation command.
        """
        result = [
            'composer', 'require',
            self.package + ":" + self.version if self.version else self.package
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        for cmd in ('composer show ' + self.package,
                    'composer global show ' + self.package):

            if not run(cmd, stdout=Capture(), stderr=Capture()).returncode:
                return True

        return False
예제 #14
0
class DistributionRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifies the
    proper type automatically.
    """
    """
    List of supported package manager with their command respectively
    """
    SUPPORTED_PACKAGE_MANAGERS = {
        'apt_get': 'apt-get',
        'brew': 'brew',
        'dnf': 'dnf',
        'pacman': 'pacman',
        'portage': 'emerge',
        'xbps': 'xbps-install',
        'yum': 'yum',
        'zypper': 'zypper',
    }
    _available_managers = None

    REQUIREMENTS = {
        AnyOneOfRequirements(
            list(
                ExecutableRequirement(name)
                for name in SUPPORTED_PACKAGE_MANAGERS.values())),
        ExecutableRequirement('grep'),
    }
    """
    List of commands that can be used to verify if the package is installed.
    """
    CHECKER_COMMANDS = {
        'apt_get': 'dpkg-query -l {}',
        'brew': 'brew list {}',
        'dnf': 'rpm -qa | grep "^{}"',
        'pacman': 'pacman -Qs {}',
        'portage': 'equery list {}',
        'xbps': 'xbps-query {}',
        'yum': 'rpm -qa | grep "^{}"',
        'zypper': 'rpm -qa | grep "^{}"',
    }
    """
    List of commands that can be used to install a package.
    """
    _INSTALL_COMMANDS = {
        'apt_get': ('apt-get', 'install', '--yes'),
        'brew': ('brew', 'install'),
        'dnf': ('dnf', 'install', '--assumeyes'),
        'pacman': ('pacman', ),
        'portage': ('emerge', ),
        'xbps': ('xbps-install', '--yes'),
        'yum': ('yum', 'install', '--assumeyes'),
        'zypper': ('zypper', '--non-interactive', 'install'),
    }

    def __init__(self,
                 package: str = None,
                 version='',
                 repo='',
                 **package_overrides):
        """
        Constructs a new ``DistributionRequirement``, using the
        ``PackageRequirement`` constructor.

        When a ``package`` name is provided, it is used as the
        package attribute, even when override package names are
        provided for specific package managers.

        >>> dr = DistributionRequirement(package='clang',
        ...                              apt_get='libclang',
        ...                              dnf='libclangg')
        >>> dr.package
        'clang'
        >>> dr.packages['apt_get']
        'libclang'
        >>> dr.packages['dnf']
        'libclangg'

        When no ``package`` name is provided, the override package name
        for the local host's package manager is used if possible,
        otherwise the most common override is used.

        >>> dr = DistributionRequirement(unknown1='libclangg',
        ...                              unknown2='libclang',
        ...                              unknown3='libclang')
        >>> dr.package
        'libclang'
        >>> dr.packages['unknown1']
        'libclangg'
        >>> dr.packages['unknown2']
        'libclang'
        >>> dr.packages['unknown3']
        'libclang'

        >>> from pprint import pprint
        >>> len(dr.REQUIREMENTS)
        2
        >>> not_grep_req = [dep for dep in dr.REQUIREMENTS
        ...                 if str(dep) != 'grep'][0]
        >>> pprint(str(not_grep_req))
        ('ExecutableRequirement(apt-get) ExecutableRequirement(brew) '
         'ExecutableRequirement(dnf) ExecutableRequirement(emerge) '
         'ExecutableRequirement(pacman) ExecutableRequirement(xbps-install) '
         'ExecutableRequirement(yum) ExecutableRequirement(zypper)')

        :param package: A string with the name of the package to be installed.
        :param version: A version string.  Unused.
        :param repo:    The repository from which the package is to be
                        installed.  Unused.
        :param kwargs: Override package names for supported package managers.
        """
        self._managers = None
        self._manager = None
        self.packages = package_overrides

        if not package and not package_overrides:
            raise NoArgsNotImplementedError('No package managers specified')

        if package:
            defaults = {(pm, package)
                        for pm in self.SUPPORTED_PACKAGE_MANAGERS.keys()
                        if pm not in package_overrides}
            self.packages.update(defaults)

        else:
            package = self._get_best_package_name()

        PackageRequirement.__init__(self, 'distribution', package, version)

    def _get_best_package_name(self):
        package_names = Counter(self.packages.values())

        if len(package_names) == 1:
            return package_names.most_common()[0][0]

        try:
            return self.packages[self.get_available_package_manager()]
        except NotImplementedError:
            return package_names.most_common()[0][0]

    def get_available_package_manager(self):
        """
        Returns the available package manager that can be used to satisfy
        the requirement.

        Raises NotImplementedError if there's no supported package manager.

        :return: Supported package manager key
        """
        if not self._manager:
            self._manager = next(self.get_package_managers())
        return self._manager

    def get_package_managers(self):
        """
        Yield package managers that can satisfy requirement.

        :raises NotImplementedError:
             There are no package managers for requirement.
        """
        found = False
        available_managers = self.available_package_managers
        supported_managers = self.SUPPORTED_PACKAGE_MANAGERS.keys()
        for manager in self.packages.keys():
            if manager in available_managers:
                found = True
                yield manager
            elif manager not in supported_managers:
                raise NotImplementedError(
                    "{} is not supported".format(manager))
        if found:
            return

        raise NotImplementedError("This platform doesn't have any of the "
                                  'specified package manager(s): '
                                  '{}'.format(','.join(self.packages.keys())))

    @property
    def package_managers(self):
        """
        Return package managers that can satisfy requirement.

        :raises NotImplementedError:
             There are no package managers for requirement.
        """
        if self._managers is None:
            self._managers = list(self.get_package_managers())
        return self._managers

    @property
    def available_package_managers(self):
        """
        Return all available package managers.

        :raises NotImplementedError:
             There are no package managers for requirement.
        """
        if self._available_managers is None:
            self._available_managers = list(
                self.get_all_available_package_managers())
        return self._available_managers

    @classmethod
    def get_all_available_package_managers(self):
        """
        Yield the available package managers.

        :raises NotImplementedError:
            There are no supported package managers.
        """
        found = False
        for manager, exe in self.SUPPORTED_PACKAGE_MANAGERS.items():
            if is_executable_exists(exe):
                found = True
                yield manager
        if found:
            return

        raise NotImplementedError(
            "This platform doesn't have any of the supported package "
            'manager(s): {}'.format(', '.join(
                self.SUPPORTED_PACKAGE_MANAGERS)))

    def is_installed(self):
        """
        Check if the requirement is satisfied by calling various package
        managers that are mentioned.

        Raises NotImplementedError if executable is not defined when
        there's no supported package manager.

        :return: True if the dependency is installed, false otherwise
        """

        package_manager = self.get_available_package_manager()
        command = self.CHECKER_COMMANDS[package_manager]
        package = self.packages[package_manager]
        return not run(command.format(package),
                       stdout=Capture(),
                       stderr=Capture()).returncode

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> DistributionRequirement('indent').install_command()[-1]
        'indent'

        :param return: A list with the installation command.
        """
        package_manager = self.get_available_package_manager()
        package = self.packages[package_manager]

        command = list(self._INSTALL_COMMANDS[package_manager])

        command.append(package)

        return command
예제 #15
0
class MavenRequirement(PackageRequirement):
    """
    This class is a subclass of ``PackageRequirement``. It specifices the proper
    type for ``java`` packages automatically and provide a function to check
    for the requirement.
    """

    REQUIREMENTS = {ExecutableRequirement('mvn')}

    def __init__(self,
                 package,
                 version: str,
                 repo="https://repo.maven.apache.org/maven2"):
        """
        Constructs a new ``MavenRequirement``, using the ``PackageRequirement``
        constructor.

        >>> mr = MavenRequirement('com.puppycrawl.tools:checkstyle', '6.15')
        >>> mr.type
        'mvn'
        >>> mr.package
        'com.puppycrawl.tools:checkstyle'
        >>> mr.version
        '6.15'
        >>> str(mr)
        'com.puppycrawl.tools:checkstyle 6.15'
        >>> mr.repo
        'https://repo.maven.apache.org/maven2'

        :param package: A string with the {groupId:artifactId} of
                        the package to be installed.
        :param version: A version string.
        :param repo:    The repository from which the package is to be
                        installed.
        """
        package_regex = '([^: /]*:[^: /]*)'
        package_match = (re.compile(package_regex)).match(package)

        if not package_match:
            raise ValueError(
                'The package must be of the form [groupId:artifactId]')

        PackageRequirement.__init__(self, 'mvn', package, version, repo)

    def install_command(self):
        """
        Creates the installation command for the instance of the class.

        >>> from pprint import pprint
        >>> r = MavenRequirement(
        ...     'com.puppycrawl.tools:checkstyle', '6.15',
        ...     'https://repo.maven.apache.org/maven2')
        >>> pprint(r.install_command())
        ['mvn',
         'dependency:get',
         '-DrepoUrl=https://repo.maven.apache.org/maven2/',
         '-Dartifact=com.puppycrawl.tools:checkstyle:6.15']


        :param return: A string with the installation command.
        """
        result = [
            'mvn', 'dependency:get', '-DrepoUrl={}/'.format(self.repo),
            '-Dartifact={}:{}'.format(self.package, self.version)
        ]
        return result

    def is_installed(self):
        """
        Checks if the dependency is installed.

        :param return: True if dependency is installed, false otherwise.
        """
        return not run('mvn dependency:get -Dartifact={}:{} --offline'.format(
            self.package, self.version),
                       stdout=Capture(),
                       stderr=Capture()).returncode