Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
choices.py230 linesDownload Raw Back to fields
1import itertools2 3from wtforms import widgets4from wtforms.fields.core import Field5from wtforms.validators import ValidationError6 7__all__ = (8    "SelectField",9    "SelectMultipleField",10    "RadioField",11)12 13 14class SelectFieldBase(Field):15    option_widget = widgets.Option()16 17    """18    Base class for fields which can be iterated to produce options.19 20    This isn't a field, but an abstract base class for fields which want to21    provide this functionality.22    """23 24    def __init__(self, label=None, validators=None, option_widget=None, **kwargs):25        super().__init__(label, validators, **kwargs)26 27        if option_widget is not None:28            self.option_widget = option_widget29 30    def iter_choices(self):31        """32        Provides data for choice widget rendering. Must return a sequence or33        iterable of (value, label, selected, render_kw) tuples.34        """35        raise NotImplementedError()36 37    def has_groups(self):38        return False39 40    def iter_groups(self):41        raise NotImplementedError()42 43    def __iter__(self):44        opts = dict(45            widget=self.option_widget,46            validators=self.validators,47            name=self.name,48            render_kw=self.render_kw,49            _form=None,50            _meta=self.meta,51        )52        for i, choice in enumerate(self.iter_choices()):53            if len(choice) == 4:54                value, label, checked, render_kw = choice55            else:56                value, label, checked = choice57                render_kw = {}58 59            opt = self._Option(60                label=label, id="%s-%d" % (self.id, i), **opts, **render_kw61            )62            opt.process(None, value)63            opt.checked = checked64            yield opt65 66    class _Option(Field):67        checked = False68 69        def _value(self):70            return str(self.data)71 72 73class SelectField(SelectFieldBase):74    widget = widgets.Select()75 76    def __init__(77        self,78        label=None,79        validators=None,80        coerce=str,81        choices=None,82        validate_choice=True,83        **kwargs,84    ):85        super().__init__(label, validators, **kwargs)86        self.coerce = coerce87        if callable(choices):88            choices = choices()89        if choices is not None:90            self.choices = choices if isinstance(choices, dict) else list(choices)91        else:92            self.choices = None93        self.validate_choice = validate_choice94 95    def iter_choices(self):96        if not self.choices:97            choices = []98        elif isinstance(self.choices, dict):99            choices = list(itertools.chain.from_iterable(self.choices.values()))100        else:101            choices = self.choices102 103        return self._choices_generator(choices)104 105    def has_groups(self):106        return isinstance(self.choices, dict)107 108    def iter_groups(self):109        if isinstance(self.choices, dict):110            for label, choices in self.choices.items():111                yield (label, self._choices_generator(choices))112 113    def _choices_generator(self, choices):114        if not choices:115            _choices = []116 117        elif isinstance(choices[0], (list, tuple)):118            _choices = choices119 120        else:121            _choices = zip(choices, choices)122 123        for value, label, *other_args in _choices:124            selected = self.coerce(value) == self.data125            render_kw = other_args[0] if len(other_args) else {}126            yield (value, label, selected, render_kw)127 128    def process_data(self, value):129        try:130            # If value is None, don't coerce to a value131            self.data = self.coerce(value) if value is not None else None132        except (ValueError, TypeError):133            self.data = None134 135    def process_formdata(self, valuelist):136        if not valuelist:137            return138 139        try:140            self.data = self.coerce(valuelist[0])141        except ValueError as exc:142            raise ValueError(self.gettext("Invalid Choice: could not coerce.")) from exc143 144    def pre_validate(self, form):145        if not self.validate_choice:146            return147 148        if self.choices is None:149            raise TypeError(self.gettext("Choices cannot be None."))150 151        for _, _, match, *_ in self.iter_choices():152            if match:153                break154        else:155            raise ValidationError(self.gettext("Not a valid choice."))156 157 158class SelectMultipleField(SelectField):159    """160    No different from a normal select field, except this one can take (and161    validate) multiple choices.  You'll need to specify the HTML `size`162    attribute to the select field when rendering.163    """164 165    widget = widgets.Select(multiple=True)166 167    def _choices_generator(self, choices):168        if not choices:169            _choices = []170 171        elif isinstance(choices[0], (list, tuple)):172            _choices = choices173 174        else:175            _choices = zip(choices, choices)176 177        for value, label, *other_args in _choices:178            selected = self.data is not None and self.coerce(value) in self.data179            render_kw = other_args[0] if len(other_args) else {}180            yield (value, label, selected, render_kw)181 182    def process_data(self, value):183        try:184            self.data = list(self.coerce(v) for v in value)185        except (ValueError, TypeError):186            self.data = None187 188    def process_formdata(self, valuelist):189        try:190            self.data = list(self.coerce(x) for x in valuelist)191        except ValueError as exc:192            raise ValueError(193                self.gettext(194                    "Invalid choice(s): one or more data inputs could not be coerced."195                )196            ) from exc197 198    def pre_validate(self, form):199        if not self.validate_choice or not self.data:200            return201 202        if self.choices is None:203            raise TypeError(self.gettext("Choices cannot be None."))204 205        acceptable = [self.coerce(choice[0]) for choice in self.iter_choices()]206        if any(data not in acceptable for data in self.data):207            unacceptable = [208                str(data) for data in set(self.data) if data not in acceptable209            ]210            raise ValidationError(211                self.ngettext(212                    "'%(value)s' is not a valid choice for this field.",213                    "'%(value)s' are not valid choices for this field.",214                    len(unacceptable),215                )216                % dict(value="', '".join(unacceptable))217            )218 219 220class RadioField(SelectField):221    """222    Like a SelectField, except displays a list of radio buttons.223 224    Iterating the field will produce subfields (each containing a label as225    well) in order to allow custom rendering of the individual radio fields.226    """227 228    widget = widgets.ListWidget(prefix_label=False)229    option_widget = widgets.RadioInput()230 
codekingpro/portable-devtools · Team Ai