Beispiel #1
0
def get_requirements_str_from_dict(requirements_dict: MappingType) -> tuple:
    '''
    Convert the passed dictionary of :mod:`setuptools`-specific requirements
    strings into a tuple of such strings.

    This dictionary is assumed to map from the :mod:`setuptools`-specific
    project name of a third-party dependency (e.g., ``NetworkX``) to the suffix
    of a :mod:`setuptools`-specific requirements string constraining this
    dependency (e.g., ``>= 1.11``). Each element of the resulting tuple is a
    string of the form `{key} {value}`, converted from a key-value pair of this
    dictionary in arbitrary order.

    Parameters
    ----------
    requirements_dict : MappingType
        Dictionary of :mod:`setuptools`-specific requirements strings in the
        format described above.

    Returns
    ----------
    tuple
        Tuple of :mod:`setuptools`-specific requirements strings in the format
        described above.
    '''

    return get_requirements_str_from_dict_keys(
        requirements_dict, *requirements_dict.keys())
Beispiel #2
0
def die_unless_values_unique(mapping: MappingType) -> None:
    '''
    Raise an exception unless all values of the passed dictionary are unique.

    Equivalently, this function raises an exception if any two key-value pairs
    of this dictionary share the same values.

    Parameters
    ----------
    mapping : MappingType
        Dictionary to be inspected.

    Raises
    ----------
    BetseMappingException
        If at least one value of this dictionary is a duplicate.
    '''

    # Avoid circular import dependencies.
    from betse.util.type.iterable import iterables
    from betse.util.type.text import strs

    # If one or more values of this dictionary are duplicates...
    if not is_values_unique(mapping):
        # Set of all duplicate values in this dictionary.
        values_duplicate = iterables.get_items_duplicate(mapping.values())

        # Raise an exception embedding this set.
        raise BetseMappingException('Dictionary values {} duplicate.'.format(
            strs.join_as_conjunction_double_quoted(*values_duplicate)))
Beispiel #3
0
    def __init__(self, key_to_dynamic_value: MappingType) -> None:
        '''
        Initialize this dictionary.

        Parameters
        ----------
        key_to_dynamic_value : MappingType
            Dictionary such that each:
            * Key is a key of the outer dictionary.
            * Value is the `DynamicValue` instance permitting that key's
              underlying variable to be dynamically get and set.
        '''

        #FIXME: Premature optimization. Reduce this to the following, please:
        #    from betse.util.type.iterable import iterables
        #    iterables.die_unless_items_instance_of(
        #        iterable=key_to_dynamic_value.values(), cls=DynamicValue)

        # If optimization is disabled, validate all values of the passed
        # dictionary to be instances of the "DynamicValue" class.
        if __debug__:
            for dynamic_value in key_to_dynamic_value.values():
                assert isinstance(
                    dynamic_value,
                    DynamicValue), ('"{}" not a dynamic value.'.format(
                        types.trim(dynamic_value)))

        # Classify the passed parameters.
        self._key_to_dynamic_value = key_to_dynamic_value
Beispiel #4
0
def is_values_unique(mapping: MappingType) -> bool:
    '''
    ``True`` only if all values of the passed dictionary are **unique** (i.e.,
    if *no* two key-value pairs of this dictionary share the same values).

    Parameters
    ----------
    mapping : MappingType
        Dictionary to be inspected.

    Returns
    ----------
    bool
        ``True`` only if *all* values of this dictionary are unique.
    '''

    # Avoid circular import dependencies.
    from betse.util.type.iterable import iterables

    # For sanity, defer to an existing low-level tester.
    return iterables.is_items_unique(mapping.values())
Beispiel #5
0
def die_unless_values_unique(mapping: MappingType) -> None:
    '''
    Raise an exception unless all values of the passed dictionary are unique.

    Equivalently, this function raises an exception if any two key-value pairs
    of this dictionary share the same values.

    Parameters
    ----------
    mapping : MappingType
        Dictionary to be validated.

    Raises
    ----------
    BetseMappingValueException
        If at least one value of this dictionary is a duplicate.

    See Also
    ----------
    :func:`is_values_unique`
        Further details.
    '''

    # Avoid circular import dependencies.
    from betse.util.type.iterable import iterget
    from betse.util.type.text.string import strjoin

    # If one or more values of this dictionary are duplicates...
    if not is_values_unique(mapping):
        # Set of all duplicate values in this dictionary.
        values_duplicate = iterget.get_items_duplicate(mapping.values())

        # Grammatically proper noun describing the number of such values.
        values_noun = 'value' if len(values_duplicate) == 1 else 'values'

        # Raise an exception embedding this set.
        raise BetseMappingValueException('Dictionary {} {} duplicate.'.format(
            values_noun,
            strjoin.join_as_conjunction_double_quoted(*values_duplicate)))
Beispiel #6
0
def die_unless_requirements_dict(requirements_dict: MappingType) -> None:
    '''
    Raise an exception unless all dependencies described by the passed
    dictionary are **satisfiable** (i.e., both importable and of a satisfactory
    version) *and* all external commands required by these dependencies (e.g.,
    GraphViz's ``dot`` command) reside in the current ``${PATH}``.

    Parameters
    ----------
    requirements_dict : MappingType
        Dictionary mapping from the names of all :mod:`setuptools`-specific
        projects implementing these dependencies to the requirements strings
        constraining these dependencies.

    Raises
    ----------
    BetseLibException
        If at least passed dependency is unsatisfiable.
    '''

    # If the "pkg_resources" setuptools dependency is missing, raise an
    # exception *BEFORE* importing this dependency below.
    pymodname.die_unless_module(
        module_name='pkg_resources',
        exception_message='Mandatory dependency "pkg_resources" not found.',
    )

    # Import the following submodule, which globally imports "pkg_resources",
    # *AFTER* validating "pkg_resources" to be importable.
    from betse.lib.setuptools import setuptool

    # Validate these dependencies.
    setuptool.die_unless_requirements_dict(requirements_dict)

    # Validate all external commands required by these dependencies.
    die_unless_command(*requirements_dict.keys())
Beispiel #7
0
def invert_map_unique(mapping: MappingType) -> MappingType:
    '''
    Dictionary inverted from the passed dictionary if no two key-value pairs of
    this dictionary share the same values *or* raise an exception otherwise.

    Specifically, the returned dictionary maps from each value to each key of
    the passed dictionary *and* is guaranteed to be the same type as that of
    the passed dictionary.

    Parameters
    ----------
    mapping : MappingType
        Dictionary to be inverted. The type of this dictionary *must* define an
        ``__init__`` method accepting a single parameter whose value is an
        iterable of 2-iterables ``(key, value)`` providing all key-value pairs
        with which to initialize a new such dictionary. See the
        :meth:`dict.__init__` method for further details.

    Returns
    ----------
    MappingType
        Dictionary inverted from this dictionary as detailed above.

    Raises
    ----------
    BetseMappingException
        If one or more key-value pairs of this dictionary share the same
        values.

    See Also
    ----------
    https://stackoverflow.com/a/1679702/2809027
        StackOverflow answer strongly inspiring this implementation.
    '''

    # Avoid circular import dependencies.
    from betse.util.type.iterable.mapping import maptest

    # If any values of this dictionary are are duplicates, raise an exception.
    maptest.die_unless_values_unique(mapping)

    # Type of this dictionary.
    mapping_type = type(mapping)

    # If this is an unordered dictionary, return a dictionary comprehension
    # efficiently inverting this dictionary in the trivial way.
    if mapping_type is dict:
        return {value: key for key, value in mapping.items()}
    # Else, this is possibly an ordered dictionary. In this case, a
    # considerably less trivial and slightly less efficient approach is needed.
    else:
        # Iterable of reversed 2-iterables "(value, pair)" for each key-value
        # pair of the passed dictionary. Dismantled, this is:
        #
        # * "mapping.items()", an iterable of 2-iterables "(key, value)" for
        #   each key-value pair of the passed dictionary.
        # * "reversed", a builtin which when passed such a 2-iterable returns
        #   the reversed 2-iterable "(value, pair)" for that key-value pair.
        # * "map(...)", a builtin applying the prior builtin to each such pair.
        value_key_pairs = map(reversed, mapping.items())

        # Return a new instance of this type of dictionary by invoking the
        # "dict(iterable)" form of this type's __init__() method. To quote the
        # dict.__init__() docstring:
        #
        # "dict(iterable) -> new dictionary initialized as if via:
        #      d = {}
        #      for k, v in iterable:
        #          d[k] = v"
        return mapping_type(value_key_pairs)
Beispiel #8
0
def get_key_value_or_default(
    # Mandatory parameters.
    mapping: MappingType,
    key: HashableType,
    value_default: object,

    # Optional parameters.
    value_type: TestableOrNoneTypes = None,
) -> object:
    '''
    Value of the passed key in the passed mapping if this mapping contains this
    key *or* the passed default value otherwise (i.e., if this mapping contains
    no such key), optionally validated to be of the passed type.

    Parameters
    ----------
    mapping : MappingType
        Dictionary to be inspected.
    key : HashableType
        Key to return the current value of.
    value_default : object
        Default value to be returned if this dictionary contains no such key.
    value_type : TestableOrNoneTypes
        Expected type of the current value of this key. This function
        effectively performs the equivalent of the :meth:`type_check` decorator
        at runtime by raising an exception if all of the following apply:

        * This type is *not* ``None``.
        * This value is *not* this default value, implying this dictionary to
          contain this key.
        * This value is *not* an instance of this type.

        Defaults to ``None``, in which case no such type checking is performed.

    Returns
    ----------
    object
        Either:

        * If this dictionary contains this key, this key's value.
        * Else, this default value.

    Raises
    ----------
    BetseTypeException
        If the ``value_type`` parameter is non-``None`` and the type of the
        current value of this key is *not* an instance of ``value_type``.
    '''

    # Avoid circular import dependencies.
    from betse.util.type.obj import objtest

    # Value of this key in this mapping if any *OR* this default value.
    key_value = mapping.get(key, value_default)

    # If this value is to be type-checked *AND* is *NOT* this default value
    # (which by definition already satisfies caller requirements regardless of
    # type), type-check this value.
    if value_type is not None and key_value is not value_default:
        objtest.die_unless_instance(obj=key_value, cls=value_type)

    # Return this value.
    return key_value