##// END OF EJS Templates
Added tag v5.3.0 for changeset 77c2b73602b9
Added tag v5.3.0 for changeset 77c2b73602b9

File last commit:

r4951:9268931e default
r5559:80b03e72 stable
Show More
__init__.py
136 lines | 5.3 KiB | text/x-python | PythonLexer
# 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 <option> and/or
<optgroup> 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 <select>.
"""
_set_id_attr(attrs, id, name)
attrs["name"] = name
prompt = attrs.pop("prompt", None)
if prompt or not isinstance(options, Options):
options = Options(options, prompt=prompt)
return HTML.tag("select", NL, options.render(selected_values), **attrs)