def test_filter_mapping(self): """ Tests :func:`colour.utilities.common.filter_mapping` definition. """ class Element(object): """ :func:`filter_mapping` unit tests :class:`Element` class. """ def __init__(self, name): self.name = name mapping = { 'Element A': Element('A'), 'Element B': Element('B'), 'Element C': Element('C'), 'Not Element C': Element('Not C'), } self.assertListEqual( sorted(filter_mapping(mapping, '\\w+\\s+A')), ['Element A']) self.assertListEqual( sorted(filter_mapping(mapping, 'Element.*')), [ 'Element A', 'Element B', 'Element C', ]) self.assertListEqual( sorted(filter_mapping(mapping, '^Element.*')), [ 'Element A', 'Element B', 'Element C', ]) self.assertListEqual( sorted(filter_mapping(mapping, '^Element.*', False)), [ 'Element A', 'Element B', 'Element C', ]) self.assertListEqual( sorted(filter_mapping(mapping, ['.*A', '.*B'])), [ 'Element A', 'Element B', ]) self.assertIsInstance( filter_mapping(mapping, '^Element.*', False), type(mapping)) self.assertIsInstance( filter_mapping(OrderedDict(mapping), '^Element.*', False), OrderedDict)
def filter_passthrough(mapping, filterers, anchors=True, allow_non_siblings=True, flags=re.IGNORECASE): """ Returns mapping objects matching given filterers while passing through class instances whose type is one of the mapping element types. This definition allows passing custom but compatible objects to the various plotting definitions that by default expect the key from a dataset element. For example, a typical call to :func:`colour.plotting.\ plot_multi_illuminant_sds` definition is as follows: >>> import colour >>> import colour.plotting >>> colour.plotting.plot_multi_illuminant_sds(['A']) ... # doctest: +SKIP But it is also possible to pass a custom spectral distribution as follows: >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> colour.plotting.plot_multi_illuminant_sds( ... ['A', colour.SpectralDistribution(data)]) ... # doctest: +SKIP Similarly, a typical call to :func:`colour.plotting.\ plot_planckian_locus_in_chromaticity_diagram_CIE1931` definition is as follows: >>> colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931( ... ['A']) ... # doctest: +SKIP But it is also possible to pass a custom whitepoint as follows: >>> colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931( ... ['A', {'Custom': np.array([1 / 3 + 0.05, 1 / 3 + 0.05])}]) ... # doctest: +SKIP Parameters ---------- mapping : dict_like Mapping to filter. filterers : unicode or object or array_like Filterer or object class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. anchors : bool, optional Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. allow_non_siblings : bool, optional Whether to allow non-siblings to be also passed through. flags : int, optional Regex flags. Returns ------- dict_like Filtered mapping. """ if is_string(filterers): filterers = [filterers] elif not isinstance(filterers, (list, tuple)): filterers = [filterers] string_filterers = [ filterer for filterer in filterers if is_string(filterer) ] object_filterers = [ filterer for filterer in filterers if is_sibling(filterer, mapping) ] if allow_non_siblings: non_siblings = [ filterer for filterer in filterers if filterer not in string_filterers and filterer not in object_filterers ] if non_siblings: runtime_warning( 'Non-sibling elements are passed-through: "{0}"'.format( non_siblings)) object_filterers.extend(non_siblings) filtered_mapping = filter_mapping(mapping, string_filterers, anchors, flags) for filterer in object_filterers: if isinstance(filterer, (dict, OrderedDict, CaseInsensitiveMapping)): for key, value in filterer.items(): filtered_mapping[key] = value else: try: name = filterer.name except AttributeError: try: name = filterer.__name__ except AttributeError: name = str(id(filterer)) filtered_mapping[name] = filterer return filtered_mapping
def filter_passthrough(mapping, filterers, anchors=True, allow_non_siblings=True, flags=re.IGNORECASE): """ Returns mapping objects matching given filterers while passing through class instances whose type is one of the mapping element types. This definition allows passing custom but compatible objects to the various plotting definitions that by default expect the key from a dataset element. For example, a typical call to :func:`colour.plotting.\ plot_multi_illuminant_sds` definition is as follows: >>> import colour >>> import colour.plotting >>> colour.plotting.plot_multi_illuminant_sds(['A']) ... # doctest: +SKIP But it is also possible to pass a custom spectral distribution as follows: >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> colour.plotting.plot_multi_illuminant_sds( ... ['A', colour.SpectralDistribution(data)]) ... # doctest: +SKIP Similarly, a typical call to :func:`colour.plotting.\ plot_planckian_locus_in_chromaticity_diagram_CIE1931` definition is as follows: >>> colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931( ... ['A']) ... # doctest: +SKIP But it is also possible to pass a custom whitepoint as follows: >>> colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931( ... ['A', {'Custom': np.array([1 / 3 + 0.05, 1 / 3 + 0.05])}]) ... # doctest: +SKIP Parameters ---------- mapping : dict_like Mapping to filter. filterers : unicode or object or array_like Filterer or object class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. anchors : bool, optional Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. allow_non_siblings : bool, optional Whether to allow non-siblings to be also passed through. flags : int, optional Regex flags. Returns ------- dict_like Filtered mapping. """ if is_string(filterers): filterers = [filterers] elif not isinstance(filterers, (list, tuple)): filterers = [filterers] string_filterers = [ filterer for filterer in filterers if is_string(filterer) ] object_filterers = [ filterer for filterer in filterers if is_sibling(filterer, mapping) ] if allow_non_siblings: non_siblings = [ filterer for filterer in filterers if filterer not in string_filterers and filterer not in object_filterers ] if non_siblings: runtime_warning( 'Non-sibling elements are passed-through: "{0}"'.format( non_siblings)) object_filterers.extend(non_siblings) filtered_mapping = filter_mapping(mapping, string_filterers, anchors, flags) for filterer in object_filterers: if isinstance(filterer, (dict, OrderedDict, CaseInsensitiveMapping)): for key, value in filterer.items(): filtered_mapping[key] = value else: try: name = filterer.name except AttributeError: try: name = filterer.__name__ except AttributeError: name = str(id(filterer)) filtered_mapping[name] = filterer return filtered_mapping
def filter_passthrough( mapping: Mapping, filterers: Union[Any, str, Sequence[Union[Any, str]]], anchors: Boolean = True, allow_non_siblings: Boolean = True, flags: Union[Integer, RegexFlag] = re.IGNORECASE, ) -> Dict: """ Return mapping objects matching given filterers while passing through class instances whose type is one of the mapping element types. This definition allows passing custom but compatible objects to the various plotting definitions that by default expect the key from a dataset element. For example, a typical call to :func:`colour.plotting.\ plot_multi_illuminant_sds` definition with a regex pattern automatically anchored at boundaries by default is as follows: >>> import colour >>> colour.plotting.plot_multi_illuminant_sds(['A']) ... # doctest: +SKIP Here, `'A'` is by default anchored at boundaries and transformed into `'^A$'`. Note that because it is a regex pattern, special characters such as parenthesis must be escaped: `'Adobe RGB (1998)'` must be written `'Adobe RGB \\(1998\\)'` instead. With the previous example, t is also possible to pass a custom spectral distribution as follows: >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> colour.plotting.plot_multi_illuminant_sds( ... ['A', colour.SpectralDistribution(data)]) ... # doctest: +SKIP Similarly, a typical call to :func:`colour.plotting.\ plot_planckian_locus_in_chromaticity_diagram_CIE1931` definition is as follows: >>> colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931( ... ['A']) ... # doctest: +SKIP But it is also possible to pass a custom whitepoint as follows: >>> colour.plotting.plot_planckian_locus_in_chromaticity_diagram_CIE1931( ... ['A', {'Custom': np.array([1 / 3 + 0.05, 1 / 3 + 0.05])}]) ... # doctest: +SKIP Parameters ---------- mapping Mapping to filter. filterers Filterer or object class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. anchors Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. allow_non_siblings Whether to allow non-siblings to be also passed through. flags Regex flags. Returns ------- :class:`dict` Filtered mapping. """ if is_string(filterers): filterers = [filterers] elif not isinstance(filterers, (list, tuple)): filterers = [filterers] string_filterers: List[str] = [ cast(str, filterer) for filterer in filterers if is_string(filterer) ] object_filterers: List[Any] = [ filterer for filterer in filterers if is_sibling(filterer, mapping) ] if allow_non_siblings: non_siblings = [ filterer for filterer in filterers if filterer not in string_filterers and filterer not in object_filterers ] if non_siblings: runtime_warning( f'Non-sibling elements are passed-through: "{non_siblings}"') object_filterers.extend(non_siblings) filtered_mapping = filter_mapping(mapping, string_filterers, anchors, flags) for filterer in object_filterers: # TODO: Consider using "MutableMapping" here. if isinstance(filterer, (dict, CaseInsensitiveMapping)): for key, value in filterer.items(): filtered_mapping[key] = value else: try: name = filterer.name except AttributeError: try: name = filterer.__name__ except AttributeError: name = str(id(filterer)) filtered_mapping[name] = filterer return filtered_mapping
def filter_passthrough(mapping, filterers, anchors=True, allow_non_siblings=True, flags=re.IGNORECASE): """ Returns mapping objects matching given filterers while passing through class instances whose type is one of the mapping element types. Parameters ---------- mapping : dict_like Mapping to filter. filterers : unicode or object or array_like Filterer or object class instance (which is passed through directly if its type is one of the mapping element types) or list of filterers. anchors : bool, optional Whether to use Regex line anchors, i.e. *^* and *$* are added, surrounding the filterers patterns. allow_non_siblings : bool, optional Whether to allow non-siblings to be also passed through. flags : int, optional Regex flags. Returns ------- dict_like Filtered mapping. """ if is_string(filterers): filterers = [filterers] elif not isinstance(filterers, (list, tuple)): filterers = [filterers] string_filterers = [ filterer for filterer in filterers if is_string(filterer) ] object_filterers = [ filterer for filterer in filterers if is_sibling(filterer, mapping) ] if allow_non_siblings: non_siblings = [ filterer for filterer in filterers if filterer not in string_filterers and filterer not in object_filterers ] if non_siblings: runtime_warning( 'Non-sibling elements are passed-through: "{0}"'.format( non_siblings)) object_filterers.extend(non_siblings) filtered_mapping = filter_mapping(mapping, string_filterers, anchors, flags) for filterer in object_filterers: try: name = filterer.name except AttributeError: try: name = filterer.__name__ except AttributeError: name = str(id(filterer)) filtered_mapping[name] = filterer return filtered_mapping