HEX
Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/8.4.25
System: Windows NT DESKTOP-4TAV2RJ 10.0 build 19045 (Windows 10) AMD64
User: fred (0)
PHP: 8.4.25
Disabled: NONE
Upload Files
File: C:/Users/fred/anaconda3/Lib/site-packages/anaconda_navigator/api/external_apps/detectors/groups.py
# -*- coding: utf-8 -*-

"""Grouping components for detector items."""

from __future__ import annotations

__all__ = ['Group', 'OptionalGroup', 'LinuxOnly', 'OsXOnly', 'WindowsOnly']

import abc
import itertools
import typing
from anaconda_navigator import config as navigator_config
from .. import exceptions
from .. import validation
from . import core


class Group(core.Source, alias='group'):  # pylint: disable=too-few-public-methods
    """
    Group of application detecting sources and filters.

    Order in which you provide sources and filters matters. Output of each source is chained with all previously
    detected applications (if such). Filters are applied to all previously detected applications.
    """

    __slots__ = ('__content',)

    def __init__(self, *args: core.Detector) -> None:
        """Initialize new :class:`~Group` instance."""
        if len(args) <= 0:
            raise TypeError('at least one source must be provided')

        if not isinstance(args[0], core.Source):
            raise TypeError(f'first argument must be a {core.Source.__qualname__!r}')

        arg: core.Detector
        for arg in args:
            if not isinstance(arg, core.Detector):
                raise TypeError(f'each argument must be a {core.Detector.__qualname__!r}, not {type(arg).__name__!r}')

        self.__content: typing.Final[typing.Tuple[core.Detector, ...]] = args

    @classmethod
    def _parse_configuration(cls, *args: typing.Any, **kwargs: typing.Any) -> core.Detector:
        """Parse configuration for this particular :class:`~Detector`."""
        new_args: typing.List[typing.Optional[core.Detector]] = []
        with exceptions.ValidationError.with_field('args'):
            validation.OfLength(at_least=1)(args)

            arg: typing.Any
            for arg in validation.iterable_items(args):
                try:
                    new_args.append(core.Detector.parse_configuration(configuration=arg))
                except exceptions.ValidationError:
                    with validation.validation_to_warning(silent=True):
                        raise exceptions.ValidationError('unsupported detector') from None
                    new_args.append(None)  # type: ignore

        if new_args[0] is not None:
            with validation.validation_to_warning(silent=True):
                validation.OfType(core.Source)(new_args[0])
        while new_args and (not isinstance(new_args[0], core.Source)):
            new_args.pop(0)
        new_args = list(filter(None, new_args))
        if len(new_args) <= 0:
            with validation.validation_to_warning(silent=False):
                raise exceptions.ValidationError('no usable source found in group')

        validation.IsEmptyMapping()(kwargs)
        with validation.catch_exception():
            return cls(*typing.cast(typing.List[core.Detector], new_args))

    def __call__(self, *, context: core.DetectorContext) -> typing.Iterator[core.DetectedApplication]:
        """Iterate through detected applications."""
        result: typing.Iterator[core.DetectedApplication] = typing.cast(core.Source, self.__content[0])(context=context)

        arg: core.Detector
        for arg in itertools.islice(self.__content, 1, len(self.__content), 1):
            if isinstance(arg, core.Source):
                result = itertools.chain(result, arg(context=context))
            elif isinstance(arg, core.Filter):
                result = arg(result, context=context)
            else:
                raise TypeError(f'unexpected detector type: {type(arg).__name__}')

        return result


class OptionalGroup(Group, metaclass=abc.ABCMeta):  # pylint: disable=too-few-public-methods
    """Group, that might be omitted if a condition isn't met."""

    __slots__ = ()

    @abc.abstractmethod
    def _check(self) -> bool:
        """Check if group should be processed."""

    def __call__(self, *, context: core.DetectorContext) -> typing.Iterator[core.DetectedApplication]:
        """Iterate through detected applications."""
        if self._check():
            return super().__call__(context=context)
        return iter(())


class LinuxOnly(OptionalGroup, alias='linux_only'):  # pylint: disable=too-few-public-methods
    """Group, that would be processed only on Linux machines."""

    __slots__ = ()

    def _check(self) -> bool:
        """Check if group should be processed."""
        return navigator_config.LINUX


class OsXOnly(OptionalGroup, alias='osx_only'):  # pylint: disable=too-few-public-methods
    """Group, that would be processed only on OS X machines."""

    __slots__ = ()

    def _check(self) -> bool:
        """Check if group should be processed."""
        return navigator_config.MAC


class WindowsOnly(OptionalGroup, alias='windows_only'):  # pylint: disable=too-few-public-methods
    """Group, that would be processed only on Windows machines."""

    __slots__ = ()

    def _check(self) -> bool:
        """Check if group should be processed."""
        return navigator_config.WIN