# backport of broken code for python 3.11 on webhelpers2 import collections from webhelpers2.misc import NotGiven from webhelpers2.html.tags import _set_id_attr, NL, _OptionsList, OptGroup, HTML class Options(_OptionsList): """A list of options and/or optgroups for a select or datalist. I'm a subclass of ``list``. My elements are ``Option`` and/or ``OptGroup`` instances. Do not add other element types or they may cause ``options.render`` or ``str(options)`` to fail. """ def __init__(self, options=None, prompt=None): """Construct an ``Options`` instance. **options**: An iterable of ``Option`` instances, ``OptGroup`` instances and/or strings. If you pass strings, they will be converted to simple ``Option`` instances (i.e., the label will be the string, and the value will be ``None``). **prompt**: If passed, this will be turned into an extra option before the others. The string argument will become the option's label, and the option's value will be the empty string. """ if prompt: self.add_option(prompt, "") if options: self._parse_options(options) def __repr__(self): classname = self.__class__.__name__ return "{0}({1!r})".format(classname, list(self)) def add_optgroup(self, label, options=None): """Create an ``OptGroup``, append it, and return it. The return value is the ``OptGroup`` instance. Call its ``.add_option`` method to add options to the group. """ group = OptGroup(label, options) self.append(group) return group def render(self, selected_values=None): """ Render the options as a concatenated literal of tags, with a newline after each. **selected_values**: The value(s) that should be preselected. You can pass a string, int, bool, or other scalar type, or a sequence of these. If you pass a scalar it will be standardized to a one-element tuple. If you don't pass anything, it will default to ``("",)`` (a tuple containing the empty string); this will correctly preselect prompts. Note that ``selected_values`` *does not do type conversions*. If you pass an int, the corresponding ``Option.value`` must also be an int or otherwise equal to it. (The actual comparision operator is ``in``.) Calling ``str(options)`` or ``options.__html__()`` is the same as calling ``options.render()`` without arguments. This is only useful if you don't want to pass any selected values. """ selected_values = self._parse_selected_values(selected_values) return self._render(self, selected_values) __str__ = __html__ = render def _render(self, options, selected_values): tags = [] for opt in options: if isinstance(opt, OptGroup): content = self._render(opt, selected_values) tag = HTML.tag("optgroup", NL, content, label=opt.label) tags.append(tag) else: value = opt.value if opt.value is not None else opt.label selected = value in selected_values tag = HTML.tag("option", opt.label, value=opt.value, selected=selected) tags.append(tag) return HTML(*tags, nl=True) @staticmethod def _parse_selected_values(values): if values is None: return ("",) is_string = isinstance(values, str) is_seq = isinstance(values, collections.abc.Sequence) if is_string or not is_seq: return (values,) else: return values def raw_select(name, selected_values, options, id=NotGiven, **attrs): """Create a dropdown selection box. Arguments: * **name**: the name of this control. * **selected_values**: the value(s) that should be preselected. A string or list of strings. Some other types are allowed; see the ``Options.render`` method for details. * **options**: an ``Options`` instance, or an iterable to pass to the its constructor. See the ``Options`` class for allowed element types. * **id**: the HTML ID attribute. This should be a keyword argument if passed. By default the ID is the same as the name. filtered through ``_make_safe_id_component()``. Pass None to suppress the ID attribute entirely. The following options may only be keyword arguments: * **multiple**: if true, this control will allow multiple selections. * **prompt**: An extra option that will be prepended to the list. The argument is the option label; e.g., "Please choose ...". The generated option's value will be the empty string (""), which is equivalent to not making a selection. If you specify this and also pass an ``Options`` instance in ``options``, it will combine the two into a new ``Options`` object rather than reusing the existing one. Any other keyword args will become HTML attributes for the