__init__.py
136 lines
| 5.3 KiB
| text/x-python
|
PythonLexer
r4951 | # 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) |