##// END OF EJS Templates
webhelpers2: added backport code not compatible with python3.11.
super-admin -
r4951:9268931e default
parent child Browse files
Show More
@@ -0,0 +1,137 b''
1 # backport of broken code for python 3.11 on webhelpers2
2
3 import collections
4 from webhelpers2.misc import NotGiven
5 from webhelpers2.html.tags import _set_id_attr, NL, _OptionsList, OptGroup, HTML
6
7
8 class Options(_OptionsList):
9 """A list of options and/or optgroups for a select or datalist.
10
11 I'm a subclass of ``list``. My elements are ``Option`` and/or ``OptGroup``
12 instances. Do not add other element types or they may cause
13 ``options.render`` or ``str(options)`` to fail.
14 """
15
16 def __init__(self, options=None, prompt=None):
17 """Construct an ``Options`` instance.
18
19 **options**: An iterable of ``Option`` instances, ``OptGroup``
20 instances and/or strings. If you pass strings, they will be converted
21 to simple ``Option`` instances (i.e., the label will be the string, and
22 the value will be ``None``).
23
24 **prompt**: If passed, this will be turned into an extra option before
25 the others. The string argument will become the option's label, and
26 the option's value will be the empty string.
27 """
28 if prompt:
29 self.add_option(prompt, "")
30 if options:
31 self._parse_options(options)
32
33 def __repr__(self):
34 classname = self.__class__.__name__
35 return "{0}({1!r})".format(classname, list(self))
36
37 def add_optgroup(self, label, options=None):
38 """Create an ``OptGroup``, append it, and return it.
39
40 The return value is the ``OptGroup`` instance. Call its
41 ``.add_option`` method to add options to the group.
42 """
43 group = OptGroup(label, options)
44 self.append(group)
45 return group
46
47 def render(self, selected_values=None):
48 """
49 Render the options as a concatenated literal of <option> and/or
50 <optgroup> tags, with a newline after each.
51
52 **selected_values**: The value(s) that should be preselected. You
53 can pass a string, int, bool, or other scalar type, or a sequence of
54 these. If you pass a scalar it will be standardized to a one-element
55 tuple. If you don't pass anything, it will default to ``("",)`` (a tuple
56 containing the empty string); this will correctly preselect prompts.
57
58 Note that ``selected_values`` *does not do type conversions*. If you
59 pass an int, the corresponding ``Option.value`` must also be an int or
60 otherwise equal to it. (The actual comparision operator is ``in``.)
61
62 Calling ``str(options)`` or ``options.__html__()`` is the same as
63 calling ``options.render()`` without arguments. This is only useful if
64 you don't want to pass any selected values.
65 """
66
67 selected_values = self._parse_selected_values(selected_values)
68 return self._render(self, selected_values)
69
70 __str__ = __html__ = render
71
72 def _render(self, options, selected_values):
73 tags = []
74 for opt in options:
75 if isinstance(opt, OptGroup):
76 content = self._render(opt, selected_values)
77 tag = HTML.tag("optgroup", NL, content, label=opt.label)
78 tags.append(tag)
79 else:
80 value = opt.value if opt.value is not None else opt.label
81 selected = value in selected_values
82 tag = HTML.tag("option", opt.label, value=opt.value, selected=selected)
83 tags.append(tag)
84 return HTML(*tags, nl=True)
85
86 @staticmethod
87 def _parse_selected_values(values):
88 if values is None:
89 return ("",)
90 is_string = isinstance(values, str)
91 is_seq = isinstance(values, collections.abc.Sequence)
92 if is_string or not is_seq:
93 return (values,)
94 else:
95 return values
96
97
98 def raw_select(name, selected_values, options, id=NotGiven, **attrs):
99 """Create a dropdown selection box.
100
101 Arguments:
102
103 * **name**: the name of this control.
104
105 * **selected_values**: the value(s) that should be preselected.
106 A string or list of strings. Some other types are allowed; see the
107 ``Options.render`` method for details.
108
109 * **options**: an ``Options`` instance, or an iterable to pass to the
110 its constructor. See the ``Options`` class for allowed element types.
111
112 * **id**: the HTML ID attribute. This should be a keyword argument if
113 passed. By default the ID is the same as the name. filtered through
114 ``_make_safe_id_component()``. Pass None to suppress the
115 ID attribute entirely.
116
117 The following options may only be keyword arguments:
118
119 * **multiple**: if true, this control will allow multiple
120 selections.
121
122 * **prompt**: An extra option that will be prepended to the list.
123 The argument is the option label; e.g., "Please choose ...". The generated
124 option's value will be the empty string (""), which is equivalent to not
125 making a selection. If you specify this and also pass an ``Options``
126 instance in ``options``, it will combine the two into a new ``Options``
127 object rather than reusing the existing one.
128
129 Any other keyword args will become HTML attributes for the <select>.
130 """
131
132 _set_id_attr(attrs, id, name)
133 attrs["name"] = name
134 prompt = attrs.pop("prompt", None)
135 if prompt or not isinstance(options, Options):
136 options = Options(options, prompt=prompt)
137 return HTML.tag("select", NL, options.render(selected_values), **attrs) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now