Ejemplo n.º 1
0
    def update_values(cls, adict):
        """
        Clients will use this method to register their values for subclasses of `~sunpy.net.attr.Attr`.

        The input has to be a dictionary, with each key being an instance of a client.
        The value for each client has to be a dictionary with each key being a subclass of Attr.
        The value for each Attr key should be a list of tuples with each tuple of the form
        ``(Name, Description)``.
        If you do not want to add a description, you can put `None` or an empty string.
        We sanitize the name you provide by removing all special characters and making it all lower case.
        If it still invalid we will append to the start of the name to make it a valid attribute name.
        This is an ugly workaround, so please try to pick a valid attribute name.

        **It is recommended that clients register values for all attrs used by their `_can_handle_query` method.**

        Parameters
        ----------
        adict : `dict`
            The keys for this dictionary have to be an instance of a downloader client.
            The values for each client are a dictionary that has keys of `~sunpy.net.attr.Attr`.
            Each key should have a list of tuples as it value.
            Each tuple should be a pair of strings.
            First string is the attribute name you want to register
            Second string is the description of the attribute.

        Examples
        --------
        # The first import is to make this example work, it should not be used otherwise
        >>> from sunpy.net.dataretriever import GenericClient
        >>> from sunpy.net import attr, attrs
        >>> attr.Attr.update_values({GenericClient : {
        ...                          attrs.Instrument: [('AIA', 'AIA is in Space.'),
        ...                                             ('HMI', 'HMI is next to AIA.')]}})   # doctest: +SKIP
        >>> attr.Attr._attr_registry[attrs.Instrument]  # doctest: +SKIP
        attr(name=['aia', 'hmi'], client=['Generic', 'Generic'], name_long=['AIA', 'HMI'],
        desc=['AIA is in Space.', 'HMI is next to AIA.'])
        >>> attr.Attr._attr_registry[attrs.Instrument].name  # doctest: +SKIP
        ['aia', 'hmi']
        >>> attr.Attr._attr_registry[attrs.Instrument].name_long  # doctest: +SKIP
        ['AIA', 'HMI']
        >>> attr.Attr._attr_registry[attrs.Instrument].desc  # doctest: +SKIP
        ['AIA is in Space.', 'HMI is next to AIA.']
        >>> attrs.Instrument.aia, attrs.Instrument.hmi  # doctest: +SKIP
        (<sunpy.net.attrs.Instrument(AIA: AIA is in Space.) object at 0x...>,
        <sunpy.net.attrs.Instrument(HMI: HMI is next to AIA.) object at 0x...>)
        """
        for client, attr_dict in adict.items():
            for attr, attr_values in attr_dict.items():
                if not isiterable(attr_values) or isinstance(attr_values, str):
                    raise ValueError(
                        f"Invalid input value: {attr_values} for key: {repr(attr)}. "
                        "The value is not iterable or just a string.")

                attr_tuple = cls._attr_registry[attr]

                for pair in attr_values:
                    if len(pair) > 2:
                        raise ValueError(
                            f'Invalid length (!=2) for values: {attr_values}.')
                    elif len(pair) == 1:
                        if pair[0] != "*":
                            raise ValueError(
                                f'Invalid value given for * registration: {attr_values}.'
                            )
                        # Special case handling for * aka all values allowed.
                        pair = [
                            "all", "All values of this type are supported."
                        ]

                    # Sanitize part one: Check if the name has a number in it
                    number_match = NUMBER_REGEX.match(pair[0])
                    p = inflect.engine()
                    try:
                        number_str = number_match.group(1)
                        name = p.number_to_words(number_str)
                        if number_str != number_match.string:
                            name = name + "_" + number_match.string[
                                number_match.end(1):]
                    except AttributeError:
                        name = pair[0]

                    # Sanitize part two: remove punctuation and replace it with _
                    name = re.sub('[%s]' % re.escape(string.punctuation), '_',
                                  name)
                    # Sanitize name, we remove all special characters
                    name = ''.join(char for char in name
                                   if char.isidentifier() or char.isnumeric())
                    # Make name lower case
                    name = name.lower()

                    if keyword.iskeyword(name):
                        # Attribute name has been appended with `_`
                        # to make it a valid identifier since its a python keyword.
                        name = name + '_'

                    attr_tuple[0].append(name)
                    attr_tuple[1].append(client.__name__.replace("Client", ""))
                    attr_tuple[2].append(pair[0])
                    attr_tuple[3].append(pair[1])
Ejemplo n.º 2
0
    def update_values(cls, adict):
        """
        Clients will use this method to register their values for subclasses of `~sunpy.net.attr.Attr`.

        The input has to be a dictionary, with each key being an instance of a client.
        The value for each client has to be a dictionary with each key being a subclass of Attr.
        The value for each Attr key should be a list of tuples with each tuple of the form (`Name`, `Description`).
        If you do not want to add a description, you can put `None` or an empty string.
        We sanitize the name you provide by removing all special characters and making it all lower case.
        If it still invalid we will append to the start of the name to make it a valid attribute name.
        This is an ugly workaround, so please try to pick a valid attribute name.

        **It is recommended that clients register values for all attrs used by their `_can_handle_query` method.**

        Parameters
        ----------
        adict : `dict`
            The keys for this dictionary have to be an instance of a downloader client.
            The values for each client are a dictionary that has keys of `~sunpy.net.attr.Attr`.
            Each key should have a list of tuples as it value.
            Each tuple should be a pair of strings.
            First string is the attribute name you want to register
            Second string is the description of the attribute.

        Example
        -------
        # The first import is to make this example work, it should not be used otherwise
        >>> from sunpy.net.dataretriever import GenericClient
        >>> from sunpy.net import attr, attrs
        >>> attr.Attr.update_values({GenericClient : {
        ...                          attrs.Instrument: [('AIA', 'AIA is in Space.'),
        ...                                             ('HMI', 'HMI is next to AIA.')]}})   # doctest: +SKIP
        >>> attr.Attr._attr_registry[attrs.Instrument]  # doctest: +SKIP
        attr(name=['aia', 'hmi'], client=['Generic', 'Generic'], name_long=['AIA', 'HMI'],
        desc=['AIA is in Space.', 'HMI is next to AIA.'])
        >>> attr.Attr._attr_registry[attrs.Instrument].name  # doctest: +SKIP
        ['aia', 'hmi']
        >>> attr.Attr._attr_registry[attrs.Instrument].name_long  # doctest: +SKIP
        ['AIA', 'HMI']
        >>> attr.Attr._attr_registry[attrs.Instrument].desc  # doctest: +SKIP
        ['AIA is in Space.', 'HMI is next to AIA.']
        >>> attrs.Instrument.aia, attrs.Instrument.hmi  # doctest: +SKIP
        (<sunpy.net.attrs.Instrument(AIA: AIA is in Space.) object at 0x...>,
        <sunpy.net.attrs.Instrument(HMI: HMI is next to AIA.) object at 0x...>)
        """
        for client in adict.keys():
            for attr, attr_values in adict[client].items():
                if isiterable(attr_values) and not isinstance(
                        attr_values, str):
                    for pair in attr_values:
                        if len(pair) != 2:
                            if len(pair) == 1:
                                # Special case handling for * aka all values allowed.
                                if pair[0] == "*":
                                    pair = [
                                        "all",
                                        "All values of this type are supported."
                                    ]
                                else:
                                    raise ValueError(
                                        f'Invalid value given for * registration: {attr_values}.'
                                    )
                            else:
                                raise ValueError(
                                    f'Invalid length (!=2) for values: {attr_values}.'
                                )
                        p = inflect.engine()
                        # Sanitize part one: Check if the name is has a number in it
                        if NUMBER_REGEX.match(pair[0]):
                            # Now check if the entire name is a number
                            if len(pair[0]) == NUMBER_REGEX.match(
                                    pair[0]).span()[1]:
                                # This turns that number into its name
                                name = p.number_to_words(pair[0])
                            # What if just the first character is
                            elif NUMBER_REGEX.match(pair[0][0]):
                                name = p.number_to_words(pair[0][0])
                                # Then we append the rest of the name here with a _ to break it up.
                                if pair[0][1:]:
                                    name = name + "_" + pair[0][1:]
                            else:
                                # Give up
                                name = pair[0]
                        else:
                            name = pair[0]
                        # Sanitize part two: remove punctuation and replace it with _
                        name = ''.join(
                            char if char not in string.punctuation else "_"
                            for char in name).lower()
                        # Sanitize name, we remove all special characters and make it all lower case
                        name = ''.join(char for char in name
                                       if char.isidentifier()
                                       or char.isnumeric()).lower()
                        if keyword.iskeyword(name):
                            # Attribute name has been appended with `_`
                            # to make it a valid identifier since its a python keyword.
                            name = name + '_'
                        if not name.isidentifier():
                            raise ValueError(f'Unable to figure out {pair}')
                        cls._attr_registry[attr][0].append(name)
                        cls._attr_registry[attr][1].append(
                            client.__name__.replace("Client", ""))
                        cls._attr_registry[attr][2].append(pair[0])
                        cls._attr_registry[attr][3].append(pair[1])
                else:
                    raise ValueError(
                        f"Invalid input value: {attr_values} for key: {repr(attr)}. "
                        "The value is not iterable or just a string.")