Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
form.py330 linesDownload Raw Back to wtforms
1import itertools2from collections import OrderedDict3 4from wtforms.meta import DefaultMeta5from wtforms.utils import unset_value6 7__all__ = ("BaseForm", "Form")8 9_default_meta = DefaultMeta()10 11 12class BaseForm:13    """14    Base Form Class.  Provides core behaviour like field construction,15    validation, and data and error proxying.16    """17 18    def __init__(self, fields, prefix="", meta=_default_meta):19        """20        :param fields:21            A dict or sequence of 2-tuples of partially-constructed fields.22        :param prefix:23            If provided, all fields will have their name prefixed with the24            value.25        :param meta:26            A meta instance which is used for configuration and customization27            of WTForms behaviors.28        """29        if prefix and prefix[-1] not in "-_;:/.":30            prefix += "-"31 32        self.meta = meta33        self._prefix = prefix34        self._fields = OrderedDict()35 36        if hasattr(fields, "items"):37            fields = fields.items()38 39        translations = self.meta.get_translations(self)40        extra_fields = []41        if meta.csrf:42            self._csrf = meta.build_csrf(self)43            extra_fields.extend(self._csrf.setup_form(self))44 45        for name, unbound_field in itertools.chain(fields, extra_fields):46            field_name = unbound_field.name or name47            options = dict(name=field_name, prefix=prefix, translations=translations)48            field = meta.bind_field(self, unbound_field, options)49            self._fields[name] = field50 51        self.form_errors = []52 53    def __iter__(self):54        """Iterate form fields in creation order."""55        return iter(self._fields.values())56 57    def __contains__(self, name):58        """Returns `True` if the named field is a member of this form."""59        return name in self._fields60 61    def __getitem__(self, name):62        """Dict-style access to this form's fields."""63        return self._fields[name]64 65    def __setitem__(self, name, value):66        """Bind a field to this form."""67        self._fields[name] = value.bind(form=self, name=name, prefix=self._prefix)68 69    def __delitem__(self, name):70        """Remove a field from this form."""71        del self._fields[name]72 73    def populate_obj(self, obj):74        """75        Populates the attributes of the passed `obj` with data from the form's76        fields.77 78        :note: This is a destructive operation; Any attribute with the same name79               as a field will be overridden. Use with caution.80        """81        for name, field in self._fields.items():82            field.populate_obj(obj, name)83 84    def process(self, formdata=None, obj=None, data=None, extra_filters=None, **kwargs):85        """Process default and input data with each field.86 87        :param formdata: Input data coming from the client, usually88            ``request.form`` or equivalent. Should provide a "multi89            dict" interface to get a list of values for a given key,90            such as what Werkzeug, Django, and WebOb provide.91        :param obj: Take existing data from attributes on this object92            matching form field attributes. Only used if ``formdata`` is93            not passed.94        :param data: Take existing data from keys in this dict matching95            form field attributes. ``obj`` takes precedence if it also96            has a matching attribute. Only used if ``formdata`` is not97            passed.98        :param extra_filters: A dict mapping field attribute names to99            lists of extra filter functions to run. Extra filters run100            after filters passed when creating the field. If the form101            has ``filter_<fieldname>``, it is the last extra filter.102        :param kwargs: Merged with ``data`` to allow passing existing103            data as parameters. Overwrites any duplicate keys in104            ``data``. Only used if ``formdata`` is not passed.105        """106        formdata = self.meta.wrap_formdata(self, formdata)107 108        if data is not None:109            kwargs = dict(data, **kwargs)110 111        filters = extra_filters.copy() if extra_filters is not None else {}112 113        for name, field in self._fields.items():114            field_extra_filters = filters.get(name, [])115 116            inline_filter = getattr(self, "filter_%s" % name, None)117            if inline_filter is not None:118                field_extra_filters.append(inline_filter)119 120            if obj is not None and hasattr(obj, name):121                data = getattr(obj, name)122            elif name in kwargs:123                data = kwargs[name]124            else:125                data = unset_value126 127            field.process(formdata, data, extra_filters=field_extra_filters)128 129    def validate(self, extra_validators=None):130        """131        Validates the form by calling `validate` on each field.132 133        :param extra_validators:134            If provided, is a dict mapping field names to a sequence of135            callables which will be passed as extra validators to the field's136            `validate` method.137 138        Returns `True` if no errors occur.139        """140        success = True141        for name, field in self._fields.items():142            if extra_validators is not None and name in extra_validators:143                extra = extra_validators[name]144            else:145                extra = tuple()146            if not field.validate(self, extra):147                success = False148        return success149 150    @property151    def data(self):152        return {name: f.data for name, f in self._fields.items()}153 154    @property155    def errors(self):156        errors = {name: f.errors for name, f in self._fields.items() if f.errors}157        if self.form_errors:158            errors[None] = self.form_errors159        return errors160 161 162class FormMeta(type):163    """164    The metaclass for `Form` and any subclasses of `Form`.165 166    `FormMeta`'s responsibility is to create the `_unbound_fields` list, which167    is a list of `UnboundField` instances sorted by their order of168    instantiation.  The list is created at the first instantiation of the form.169    If any fields are added/removed from the form, the list is cleared to be170    re-generated on the next instantiation.171 172    Any properties which begin with an underscore or are not `UnboundField`173    instances are ignored by the metaclass.174    """175 176    def __init__(cls, name, bases, attrs):177        type.__init__(cls, name, bases, attrs)178        cls._unbound_fields = None179        cls._wtforms_meta = None180 181    def __call__(cls, *args, **kwargs):182        """183        Construct a new `Form` instance.184 185        Creates the `_unbound_fields` list and the internal `_wtforms_meta`186        subclass of the class Meta in order to allow a proper inheritance187        hierarchy.188        """189        if cls._unbound_fields is None:190            fields = []191            for name in dir(cls):192                if not name.startswith("_"):193                    unbound_field = getattr(cls, name)194                    if hasattr(unbound_field, "_formfield"):195                        fields.append((name, unbound_field))196            # We keep the name as the second element of the sort197            # to ensure a stable sort.198            fields.sort(key=lambda x: (x[1].creation_counter, x[0]))199            cls._unbound_fields = fields200 201        # Create a subclass of the 'class Meta' using all the ancestors.202        if cls._wtforms_meta is None:203            bases = []204            for mro_class in cls.__mro__:205                if "Meta" in mro_class.__dict__:206                    bases.append(mro_class.Meta)207            cls._wtforms_meta = type("Meta", tuple(bases), {})208        return type.__call__(cls, *args, **kwargs)209 210    def __setattr__(cls, name, value):211        """212        Add an attribute to the class, clearing `_unbound_fields` if needed.213        """214        if name == "Meta":215            cls._wtforms_meta = None216        elif not name.startswith("_") and hasattr(value, "_formfield"):217            cls._unbound_fields = None218        type.__setattr__(cls, name, value)219 220    def __delattr__(cls, name):221        """222        Remove an attribute from the class, clearing `_unbound_fields` if223        needed.224        """225        if not name.startswith("_"):226            cls._unbound_fields = None227        type.__delattr__(cls, name)228 229 230class Form(BaseForm, metaclass=FormMeta):231    """232    Declarative Form base class. Extends BaseForm's core behaviour allowing233    fields to be defined on Form subclasses as class attributes.234 235    In addition, form and instance input data are taken at construction time236    and passed to `process()`.237    """238 239    Meta = DefaultMeta240 241    def __init__(242        self,243        formdata=None,244        obj=None,245        prefix="",246        data=None,247        meta=None,248        **kwargs,249    ):250        """251        :param formdata: Input data coming from the client, usually252            ``request.form`` or equivalent. Should provide a "multi253            dict" interface to get a list of values for a given key,254            such as what Werkzeug, Django, and WebOb provide.255        :param obj: Take existing data from attributes on this object256            matching form field attributes. Only used if ``formdata`` is257            not passed.258        :param prefix: If provided, all fields will have their name259            prefixed with the value. This is for distinguishing multiple260            forms on a single page. This only affects the HTML name for261            matching input data, not the Python name for matching262            existing data.263        :param data: Take existing data from keys in this dict matching264            form field attributes. ``obj`` takes precedence if it also265            has a matching attribute. Only used if ``formdata`` is not266            passed.267        :param meta: A dict of attributes to override on this form's268            :attr:`meta` instance.269        :param extra_filters: A dict mapping field attribute names to270            lists of extra filter functions to run. Extra filters run271            after filters passed when creating the field. If the form272            has ``filter_<fieldname>``, it is the last extra filter.273        :param kwargs: Merged with ``data`` to allow passing existing274            data as parameters. Overwrites any duplicate keys in275            ``data``. Only used if ``formdata`` is not passed.276        """277        meta_obj = self._wtforms_meta()278        if meta is not None and isinstance(meta, dict):279            meta_obj.update_values(meta)280        super().__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)281 282        for name, field in self._fields.items():283            # Set all the fields to attributes so that they obscure the class284            # attributes with the same names.285            setattr(self, name, field)286        self.process(formdata, obj, data=data, **kwargs)287 288    def __setitem__(self, name, value):289        raise TypeError("Fields may not be added to Form instances, only classes.")290 291    def __delitem__(self, name):292        del self._fields[name]293        setattr(self, name, None)294 295    def __delattr__(self, name):296        if name in self._fields:297            self.__delitem__(name)298        else:299            # This is done for idempotency, if we have a name which is a field,300            # we want to mask it by setting the value to None.301            unbound_field = getattr(self.__class__, name, None)302            if unbound_field is not None and hasattr(unbound_field, "_formfield"):303                setattr(self, name, None)304            else:305                super().__delattr__(name)306 307    def validate(self, extra_validators=None):308        """Validate the form by calling ``validate`` on each field.309        Returns ``True`` if validation passes.310 311        If the form defines a ``validate_<fieldname>`` method, it is312        appended as an extra validator for the field's ``validate``.313 314        :param extra_validators: A dict mapping field names to lists of315            extra validator methods to run. Extra validators run after316            validators passed when creating the field. If the form has317            ``validate_<fieldname>``, it is the last extra validator.318        """319        if extra_validators is not None:320            extra = extra_validators.copy()321        else:322            extra = {}323 324        for name in self._fields:325            inline = getattr(self.__class__, f"validate_{name}", None)326            if inline is not None:327                extra.setdefault(name, []).append(inline)328 329        return super().validate(extra)330 
codekingpro/portable-devtools · Team Ai