html widget classes to keep rendering order consistent we can t just iterate over items we need to sort the keys and iterate over the sorted list given a relative or absolute path to a static asset return an absolute path an absolute path will be returned unchanged while a relative path will be passed to django templatetags static static return a media object that only contains media of the given type if name in mediatypes return mediastrname getattrself name raise keyerror unknown media type s name staticmethod def mergelists ts topologicalsorter for head tail in filternone lists ts addhead ensure that the first items are included for item in tail if head item avoid circular dependency to self ts additem head head item try return listts staticorder except cycleerror warnings warn detected duplicate media files in an opposite order format joinreprlist for list in lists mediaorderconflictwarning return listdict fromkeyschain fromiterablefilternone lists def addself other combined media combined csslists self csslists combined jslists self jslists for item in other csslists if item and item not in self csslists combined csslists appenditem for item in other jslists if item and item not in self jslists combined jslists appenditem return combined def mediapropertycls def mediaself get the media property of the superclass if it exists supcls supercls self try base supcls media except attributeerror base media get the media definition for this class definition getattrcls media none if definition extend getattrdefinition extend true if extend if extend is true m base else m media for medium in extend m basemedium return m mediadefinition return mediadefinition return base return propertymedia class mediadefiningclasstype def newmcs name bases attrs newclass super newmcs name bases attrs if media not in attrs newclass media mediapropertynewclass return newclass class widgetmetaclassmediadefiningclass needsmultipartform false determines does this widget need multipart form islocalized false isrequired false supportsmicroseconds true usefieldset false def initself attrsnone self attrs if attrs is none else attrs copy def deepcopyself memo obj copy copyself obj attrs self attrs copy memoidself obj return obj property def ishiddenself return self inputtype hidden if hasattrself inputtype else false def subwidgetsself name value attrsnone context self getcontextname value attrs yield contextwidget def formatvalueself value if value or value is none return none if self islocalized return formats localizeinputvalue return strvalue def getcontextself name value attrs return widget name name ishidden self ishidden required self isrequired value self formatvaluevalue attrs self buildattrsself attrs attrs templatename self templatename def renderself name value attrsnone renderernone build an attribute dictionary return baseattrs extraattrs or def valuefromdatadictself data files name return data getname def valueomittedfromdataself data files name return name not in data def idforlabelself id return id def userequiredattributeself initial return not self ishidden class inputwidget inputtype none subclasses must define this templatename djangoformswidgetsinput html def initself attrsnone if attrs is not none attrs attrs copy self inputtype attrs poptype self inputtype super initattrs def getcontextself name value attrs context super getcontextname value attrs contextwidgettype self inputtype return context class textinputinput inputtype text templatename djangoformswidgetstext html class numberinputinput inputtype number templatename djangoformswidgetsnumber html class emailinputinput inputtype email templatename djangoformswidgetsemail html class urlinputinput inputtype url templatename djangoformswidgetsurl html class passwordinputinput inputtype password templatename djangoformswidgetspassword html def initself attrsnone rendervaluefalse super initattrs self rendervalue rendervalue def getcontextself name value attrs if not self rendervalue value none return super getcontextname value attrs class hiddeninputinput inputtype hidden templatename djangoformswidgetshidden html class multiplehiddeninputhiddeninput templatename djangoformswidgetsmultiplehidden html def getcontextself name value attrs context super getcontextname value attrs finalattrs contextwidgetattrs id contextwidgetattrs getid subwidgets for index value in enumeratecontextwidgetvalue widgetattrs finalattrs copy if id an id attribute was given add a numeric index as a suffix so that the inputs don t all have the same id attribute widgetattrsid ss id index widget hiddeninput widget isrequired self isrequired subwidgets appendwidget getcontextname value widgetattrswidget contextwidgetsubwidgets subwidgets return context def valuefromdatadictself data files name try getter data getlist except attributeerror getter data get return gettername def formatvalueself value return if value is none else value class fileinputinput allowmultipleselected false inputtype file needsmultipartform true templatename djangoformswidgetsfile html def initself attrsnone if attrs is not none and not self allowmultipleselected and attrs getmultiple false raise valueerror s doesn t support uploading multiple files self class qualname if self allowmultipleselected if attrs is none attrs multiple true else attrs setdefaultmultiple true super initattrs def formatvalueself value given the name of the file input return the name of the clear checkbox input given the name of the clear checkbox input return the html id for it return whether value is considered to be initial value return the file object if it has a defined url attribute if the user contradicts themselves uploads a new file and checks the clear checkbox we return a unique marker object that filefield will turn into a validationerror false signals to clear any existing value as opposed to just none use slightly better defaults than html s 20x2 box defined at module level so that checkboxinput is picklable 17976 checktest is a callable that takes a value and returns true if the checkbox should be checked for that value only return the value attribute if value isn t empty if value is true or value is false or value is none or value return return strvalue def getcontextself name value attrs if self checktestvalue attrs attrs or checked true return super getcontextname value attrs def valuefromdatadictself data files name if name not in data a missing value means false because html form submission does not send results for unselected checkboxes return false value data getname translate true and false strings to boolean values values true true false false if isinstancevalue str value values getvalue lower value return boolvalue def valueomittedfromdataself data files name html checkboxes don t appear in post data if not checked so it s never known if the value is actually omitted return false class choicewidgetwidget allowmultipleselected false inputtype none templatename none optiontemplatename none addidindex true checkedattribute checked true optioninheritsattrs true def initself attrsnone choices super initattrs self choices choices def deepcopyself memo obj copy copyself obj attrs self attrs copy obj choices copy copyself choices memoidself obj return obj def subwidgetsself name value attrsnone value self formatvaluevalue yield from self optionsname value attrs def optionsself name value attrsnone return a list of optgroups for this widget groups hasselected false for index optionvalue optionlabel in enumerateself choices if optionvalue is none optionvalue subgroup if isinstanceoptionlabel list tuple groupname optionvalue subindex 0 choices optionlabel else groupname none subindex none choices optionvalue optionlabel groups appendgroupname subgroup index for subvalue sublabel in choices selected not hasselected or self allowmultipleselected and str subvalue in value hasselected selected subgroup append self createoption name subvalue sublabel selected index subindexsubindex attrsattrs if subindex is not none subindex 1 return groups def createoption self name value label selected index subindexnone attrsnone index strindex if subindex is none else ss index subindex optionattrs self buildattrsself attrs attrs if self optioninheritsattrs else if selected optionattrs updateself checkedattribute if id in optionattrs optionattrsid self idforlabeloptionattrsid index return name name value value label label selected selected index index attrs optionattrs type self inputtype templatename self optiontemplatename wraplabel true def getcontextself name value attrs context super getcontextname value attrs contextwidgetoptgroups self optgroups name contextwidgetvalue attrs return context def idforlabelself id index0 if id and self addidindex id ss id index return id def valuefromdatadictself data files name getter data get if self allowmultipleselected try getter data getlist except attributeerror pass return gettername def formatvalueself value return true if the choice s value is empty string or none value choice return value is none or value def userequiredattributeself initial userequiredattribute super userequiredattributeinitial required is always okay for select multiple if self allowmultipleselected return userequiredattribute firstchoice nextiterself choices none return userequiredattribute and firstchoice is not none and self choicehasemptyvaluefirstchoice class nullbooleanselectselect def initself attrsnone choices unknown unknown true yes false no super initattrs choices def formatvalueself value try return true true false false true true false false for backwards compatibility with django 2 2 2 true 3 false value except keyerror return unknown def valuefromdatadictself data files name value data getname return true true true true false false false false true true false false for backwards compatibility with django 2 2 2 true 3 false getvalue class selectmultipleselect allowmultipleselected true def valuefromdatadictself data files name try getter data getlist except attributeerror getter data get return gettername def valueomittedfromdataself data files name an unselected select multiple doesn t appear in post data so it s never known if the value is actually omitted return false class radioselectchoicewidget inputtype radio templatename djangoformswidgetsradio html optiontemplatename djangoformswidgetsradiooption html usefieldset true def idforlabelself id indexnone if index is none return return super idforlabelid index class checkboxselectmultipleradioselect allowmultipleselected true inputtype checkbox templatename djangoformswidgetscheckboxselect html optiontemplatename djangoformswidgetscheckboxoption html def userequiredattributeself initial don t use the required attribute because browser validation would require all checkboxes to be checked instead of at least one return false def valueomittedfromdataself data files name html checkboxes don t appear in post data if not checked so it s never known if the value is actually omitted return false class multiwidgetwidget templatename djangoformswidgetsmultiwidget html usefieldset true def initself widgets attrsnone if isinstancewidgets dict self widgetsnames s name if name else for name in widgets widgets widgets values else self widgetsnames s i for i in rangelenwidgets self widgets w if isinstancew type else w for w in widgets super initattrs property def ishiddenself return allw ishidden for w in self widgets def getcontextself name value attrs context super getcontextname value attrs if self islocalized for widget in self widgets widget islocalized self islocalized value is a listtuple of values each corresponding to a widget in self widgets if not isinstancevalue list tuple value self decompressvalue finalattrs contextwidgetattrs inputtype finalattrs poptype none id finalattrs getid subwidgets for i widgetname widget in enumerate zipself widgetsnames self widgets if inputtype is not none widget inputtype inputtype widgetname name widgetname try widgetvalue valuei except indexerror widgetvalue none if id widgetattrs finalattrs copy widgetattrsid ss id i else widgetattrs finalattrs subwidgets append widget getcontextwidgetname widgetvalue widgetattrswidget contextwidgetsubwidgets subwidgets return context def idforlabelself id return def valuefromdatadictself data files name return widget valuefromdatadictdata files name widgetname for widgetname widget in zipself widgetsnames self widgets def valueomittedfromdataself data files name return all widget valueomittedfromdatadata files name widgetname for widgetname widget in zipself widgetsnames self widgets def decompressself value raise notimplementederrorsubclasses must implement this method def getmediaself media media for w in self widgets media w media return media media propertygetmedia def deepcopyself memo obj super deepcopymemo obj widgets copy deepcopyself widgets return obj property def needsmultipartformself return anyw needsmultipartform for w in self widgets class splitdatetimewidgetmultiwidget supportsmicroseconds false templatename djangoformswidgetssplitdatetime html def init self attrsnone dateformatnone timeformatnone dateattrsnone timeattrsnone widgets dateinput attrsattrs if dateattrs is none else dateattrs formatdateformat timeinput attrsattrs if timeattrs is none else timeattrs formattimeformat super initwidgets def decompressself value if value value tocurrenttimezonevalue return value date value time return none none class splithiddendatetimewidgetsplitdatetimewidget templatename djangoformswidgetssplithiddendatetime html def init self attrsnone dateformatnone timeformatnone dateattrsnone timeattrsnone super initattrs dateformat timeformat dateattrs timeattrs for widget in self widgets widget inputtype hidden class selectdatewidgetwidget nonevalue monthfield smonth dayfield sday yearfield syear templatename djangoformswidgetsselectdate html inputtype select selectwidget select datere lazyrecompilerd40dd dd usefieldset true def initself attrsnone yearsnone monthsnone emptylabelnone self attrs attrs or optional list or tuple of years to use in the year select box if years self years years else thisyear datetime date today year self years rangethisyear thisyear 10 optional dict of months to use in the month select box if months self months months else self months months optional string list or tuple to use as emptylabel if isinstanceemptylabel list tuple if not lenemptylabel 3 raise valueerroremptylabel listtuple must have 3 elements self yearnonevalue emptylabel0 self monthnonevalue emptylabel1 self daynonevalue emptylabel2 else if emptylabel is not none self nonevalue emptylabel self yearnonevalue self nonevalue self monthnonevalue self nonevalue self daynonevalue self nonevalue def getcontextself name value attrs context super getcontextname value attrs datecontext yearchoices i stri for i in self years if not self isrequired yearchoices insert0 self yearnonevalue yearname self yearfield name datecontextyear self selectwidget attrs choicesyearchoices getcontext nameyearname valuecontextwidgetvalueyear attrscontextwidgetattrs id ids yearname monthchoices listself months items if not self isrequired monthchoices insert0 self monthnonevalue monthname self monthfield name datecontextmonth self selectwidget attrs choicesmonthchoices getcontext namemonthname valuecontextwidgetvaluemonth attrscontextwidgetattrs id ids monthname daychoices i i for i in range1 32 if not self isrequired daychoices insert0 self daynonevalue dayname self dayfield name datecontextday self selectwidget attrs choicesdaychoices getcontext namedayname valuecontextwidgetvalueday attrscontextwidgetattrs id ids dayname subwidgets for field in self parsedatefmt subwidgets appenddatecontextfieldwidget contextwidgetsubwidgets subwidgets return context def formatvalueself value year month day none none none if isinstancevalue datetime date datetime datetime year month day value year value month value day elif isinstancevalue str match self datere matchvalue if match convert any zeros in the date to empty strings to match the empty option value year month day intval or for val in match groups else inputformat getformatdateinputformats0 try d datetime datetime strptimevalue inputformat except valueerror pass else year month day d year d month d day return year year month month day day staticmethod def parsedatefmt fmt getformatdateformat escaped false for char in fmt if escaped escaped false elif char escaped true elif char in yy yield year elif char in befmmnn yield month elif char in dj yield day def idforlabelself id for firstselect in self parsedatefmt return ss id firstselect return smonth id def valuefromdatadictself data files name y data getself yearfield name m data getself monthfield name d data getself dayfield name if y m d return none if y is not none and m is not none and d is not none inputformat getformatdateinputformats0 inputformat formats sanitizestrftimeformatinputformat try datevalue datetime dateinty intm intd except valueerror return pseudoiso dates with zeros for any unselected values e g 2017023 return sss y or 0 m or 0 d or 0 except overflowerror return 000 return datevalue strftimeinputformat return data getname def valueomittedfromdataself data files name return not any formatname interval in data for interval in year month day to keep rendering order consistent we can t just iterate over items we need to sort the keys and iterate over the sorted list given a relative or absolute path to a static asset return an absolute path an absolute path will be returned unchanged while a relative path will be passed to django templatetags static static return a media object that only contains media of the given type merge lists while trying to keep the relative order of the elements warn if the lists have the same elements in a different relative order for static assets it can be important to have them included in the dom in a certain order in javascript you may not be able to reference a global or in css you might want to override a style ensure that the first items are included avoid circular dependency to self get the media property of the superclass if it exists get the media definition for this class metaclass for classes that can have media definitions determines does this widget need multipart form return a value as it should appear when rendered in a template render the widget as an html string build an attribute dictionary given a dictionary of data and this widget s name return the value of this widget or none if it s not provided return the html id attribute of this widget for use by a label given the id of the field return an empty string if no id is available this hook is necessary because some widgets have multiple html elements and thus multiple ids in that case this method should return an id value that corresponds to the first id in the widget s tags base class for all input widgets subclasses must define this handle input type hidden for fields that have a list of values an id attribute was given add a numeric index as a suffix so that the inputs don t all have the same id attribute file input never renders a value given the name of the file input return the name of the clear checkbox input given the name of the clear checkbox input return the html id for it return whether value is considered to be initial value return the file object if it has a defined url attribute if the user contradicts themselves uploads a new file and checks the clear checkbox we return a unique marker object that filefield will turn into a validationerror false signals to clear any existing value as opposed to just none use slightly better defaults than html s 20x2 box defined at module level so that checkboxinput is picklable 17976 check_test is a callable that takes a value and returns true if the checkbox should be checked for that value only return the value attribute if value isn t empty a missing value means false because html form submission does not send results for unselected checkboxes translate true and false strings to boolean values html checkboxes don t appear in post data if not checked so it s never known if the value is actually omitted yield all subwidgets of this widget used to enable iterating options from a boundfield for choice widgets yield a flat list of options for this widget return a list of optgroups for this widget use an incremented id for each option where the main widget references the zero index return selected values as a list return true if the choice s value is empty string or none don t render required if the first option has a value as that s invalid html required is always okay for select multiple a select widget intended to be used with nullbooleanfield for backwards compatibility with django 2 2 for backwards compatibility with django 2 2 an unselected select multiple doesn t appear in post data so it s never known if the value is actually omitted don t include for field_0 in label to improve accessibility when using a screen reader in addition clicking such a label would toggle the first input don t use the required attribute because browser validation would require all checkboxes to be checked instead of at least one html checkboxes don t appear in post data if not checked so it s never known if the value is actually omitted a widget that is composed of multiple widgets in addition to the values added by widget get_context this widget adds a list of subwidgets to the context as widget subwidgets these can be looped over and rendered like normal widgets you ll probably want to use this class with multivaluefield value is a list tuple of values each corresponding to a widget in self widgets return a list of decompressed values for the given compressed value the given value can be assumed to be valid but not necessarily non empty media for a multiwidget is the combination of all media of the subwidgets a widget that splits datetime input into two input type text boxes a widget that splits datetime input into two input type hidden inputs a widget that splits date input into three select boxes this also serves as an example of a widget that has more than one html element and hence implements value_from_datadict optional list or tuple of years to use in the year select box optional dict of months to use in the month select box optional string list or tuple to use as empty_label return a dict containing the year month and day of the current value use dict instead of a datetime to allow invalid dates such as february 31 to display correctly convert any zeros in the date to empty strings to match the empty option value return pseudo iso dates with zeros for any unselected values e g 2017 0 23
import copy
import datetime
import warnings
from collections import defaultdict
from graphlib import CycleError, TopologicalSorter
from itertools import chain

from django.forms.utils import to_current_timezone
from django.templatetags.static import static
from django.utils import formats
from django.utils.choices import normalize_choices
from django.utils.dates import MONTHS
from django.utils.formats import get_format
from django.utils.html import format_html, html_safe
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

from .renderers import get_default_renderer

__all__ = (
    "Media",
    "MediaDefiningClass",
    "Widget",
    "TextInput",
    "NumberInput",
    "EmailInput",
    "URLInput",
    "PasswordInput",
    "HiddenInput",
    "MultipleHiddenInput",
    "FileInput",
    "ClearableFileInput",
    "Textarea",
    "DateInput",
    "DateTimeInput",
    "TimeInput",
    "CheckboxInput",
    "Select",
    "NullBooleanSelect",
    "SelectMultiple",
    "RadioSelect",
    "CheckboxSelectMultiple",
    "MultiWidget",
    "SplitDateTimeWidget",
    "SplitHiddenDateTimeWidget",
    "SelectDateWidget",
)

MEDIA_TYPES = ("css", "js")


class MediaOrderConflictWarning(RuntimeWarning):
    pass


@html_safe
class Media:
    def __init__(self, media=None, css=None, js=None):
        if media is not None:
            css = getattr(media, "css", {})
            js = getattr(media, "js", [])
        else:
            if css is None:
                css = {}
            if js is None:
                js = []
        self._css_lists = [css]
        self._js_lists = [js]

    def __repr__(self):
        return "Media(css=%r, js=%r)" % (self._css, self._js)

    def __str__(self):
        return self.render()

    @property
    def _css(self):
        css = defaultdict(list)
        for css_list in self._css_lists:
            for medium, sublist in css_list.items():
                css[medium].append(sublist)
        return {medium: self.merge(*lists) for medium, lists in css.items()}

    @property
    def _js(self):
        return self.merge(*self._js_lists)

    def render(self):
        return mark_safe(
            "\n".join(
                chain.from_iterable(
                    getattr(self, "render_" + name)() for name in MEDIA_TYPES
                )
            )
        )

    def render_js(self):
        return [
            path.__html__()
            if hasattr(path, "__html__")
            else format_html('<script src="{}"></script>', self.absolute_path(path))
            for path in self._js
        ]

    def render_css(self):
        
        
        media = sorted(self._css)
        return chain.from_iterable(
            [
                path.__html__()
                if hasattr(path, "__html__")
                else format_html(
                    '<link href="{}" media="{}" rel="stylesheet">',
                    self.absolute_path(path),
                    medium,
                )
                for path in self._css[medium]
            ]
            for medium in media
        )

    def absolute_path(self, path):
        
        if path.startswith(("http://", "https://", "/")):
            return path
        return static(path)

    def __getitem__(self, name):
        
        if name in MEDIA_TYPES:
            return Media(**{str(name): getattr(self, "_" + name)})
        raise KeyError('Unknown media type "%s"' % name)

    @staticmethod
    def merge(*lists):
        
        ts = TopologicalSorter()
        for head, *tail in filter(None, lists):
            ts.add(head)  
            for item in tail:
                if head != item:  
                    ts.add(item, head)
                head = item
        try:
            return list(ts.static_order())
        except CycleError:
            warnings.warn(
                "Detected duplicate Media files in an opposite order: {}".format(
                    ", ".join(repr(list_) for list_ in lists)
                ),
                MediaOrderConflictWarning,
            )
            return list(dict.fromkeys(chain.from_iterable(filter(None, lists))))

    def __add__(self, other):
        combined = Media()
        combined._css_lists = self._css_lists[:]
        combined._js_lists = self._js_lists[:]
        for item in other._css_lists:
            if item and item not in self._css_lists:
                combined._css_lists.append(item)
        for item in other._js_lists:
            if item and item not in self._js_lists:
                combined._js_lists.append(item)
        return combined


def media_property(cls):
    def _media(self):
        
        sup_cls = super(cls, self)
        try:
            base = sup_cls.media
        except AttributeError:
            base = Media()

        
        definition = getattr(cls, "Media", None)
        if definition:
            extend = getattr(definition, "extend", True)
            if extend:
                if extend is True:
                    m = base
                else:
                    m = Media()
                    for medium in extend:
                        m += base[medium]
                return m + Media(definition)
            return Media(definition)
        return base

    return property(_media)


class MediaDefiningClass(type):
    

    def __new__(mcs, name, bases, attrs):
        new_class = super().__new__(mcs, name, bases, attrs)

        if "media" not in attrs:
            new_class.media = media_property(new_class)

        return new_class


class Widget(metaclass=MediaDefiningClass):
    needs_multipart_form = False  
    is_localized = False
    is_required = False
    supports_microseconds = True
    use_fieldset = False

    def __init__(self, attrs=None):
        self.attrs = {} if attrs is None else attrs.copy()

    def __deepcopy__(self, memo):
        obj = copy.copy(self)
        obj.attrs = self.attrs.copy()
        memo[id(self)] = obj
        return obj

    @property
    def is_hidden(self):
        return self.input_type == "hidden" if hasattr(self, "input_type") else False

    def subwidgets(self, name, value, attrs=None):
        context = self.get_context(name, value, attrs)
        yield context["widget"]

    def format_value(self, value):
        
        if value == "" or value is None:
            return None
        if self.is_localized:
            return formats.localize_input(value)
        return str(value)

    def get_context(self, name, value, attrs):
        return {
            "widget": {
                "name": name,
                "is_hidden": self.is_hidden,
                "required": self.is_required,
                "value": self.format_value(value),
                "attrs": self.build_attrs(self.attrs, attrs),
                "template_name": self.template_name,
            },
        }

    def render(self, name, value, attrs=None, renderer=None):
        
        context = self.get_context(name, value, attrs)
        return self._render(self.template_name, context, renderer)

    def _render(self, template_name, context, renderer=None):
        if renderer is None:
            renderer = get_default_renderer()
        return mark_safe(renderer.render(template_name, context))

    def build_attrs(self, base_attrs, extra_attrs=None):
        
        return {**base_attrs, **(extra_attrs or {})}

    def value_from_datadict(self, data, files, name):
        
        return data.get(name)

    def value_omitted_from_data(self, data, files, name):
        return name not in data

    def id_for_label(self, id_):
        
        return id_

    def use_required_attribute(self, initial):
        return not self.is_hidden


class Input(Widget):
    

    input_type = None  
    template_name = "django/forms/widgets/input.html"

    def __init__(self, attrs=None):
        if attrs is not None:
            attrs = attrs.copy()
            self.input_type = attrs.pop("type", self.input_type)
        super().__init__(attrs)

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        context["widget"]["type"] = self.input_type
        return context


class TextInput(Input):
    input_type = "text"
    template_name = "django/forms/widgets/text.html"


class NumberInput(Input):
    input_type = "number"
    template_name = "django/forms/widgets/number.html"


class EmailInput(Input):
    input_type = "email"
    template_name = "django/forms/widgets/email.html"


class URLInput(Input):
    input_type = "url"
    template_name = "django/forms/widgets/url.html"


class PasswordInput(Input):
    input_type = "password"
    template_name = "django/forms/widgets/password.html"

    def __init__(self, attrs=None, render_value=False):
        super().__init__(attrs)
        self.render_value = render_value

    def get_context(self, name, value, attrs):
        if not self.render_value:
            value = None
        return super().get_context(name, value, attrs)


class HiddenInput(Input):
    input_type = "hidden"
    template_name = "django/forms/widgets/hidden.html"


class MultipleHiddenInput(HiddenInput):
    

    template_name = "django/forms/widgets/multiple_hidden.html"

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        final_attrs = context["widget"]["attrs"]
        id_ = context["widget"]["attrs"].get("id")

        subwidgets = []
        for index, value_ in enumerate(context["widget"]["value"]):
            widget_attrs = final_attrs.copy()
            if id_:
                
                
                widget_attrs["id"] = "%s_%s" % (id_, index)
            widget = HiddenInput()
            widget.is_required = self.is_required
            subwidgets.append(widget.get_context(name, value_, widget_attrs)["widget"])

        context["widget"]["subwidgets"] = subwidgets
        return context

    def value_from_datadict(self, data, files, name):
        try:
            getter = data.getlist
        except AttributeError:
            getter = data.get
        return getter(name)

    def format_value(self, value):
        return [] if value is None else value


class FileInput(Input):
    allow_multiple_selected = False
    input_type = "file"
    needs_multipart_form = True
    template_name = "django/forms/widgets/file.html"

    def __init__(self, attrs=None):
        if (
            attrs is not None
            and not self.allow_multiple_selected
            and attrs.get("multiple", False)
        ):
            raise ValueError(
                "%s doesn't support uploading multiple files."
                % self.__class__.__qualname__
            )
        if self.allow_multiple_selected:
            if attrs is None:
                attrs = {"multiple": True}
            else:
                attrs.setdefault("multiple", True)
        super().__init__(attrs)

    def format_value(self, value):
        
        return

    def value_from_datadict(self, data, files, name):
        "File widgets take data from FILES, not POST"
        getter = files.get
        if self.allow_multiple_selected:
            try:
                getter = files.getlist
            except AttributeError:
                pass
        return getter(name)

    def value_omitted_from_data(self, data, files, name):
        return name not in files

    def use_required_attribute(self, initial):
        return super().use_required_attribute(initial) and not initial


FILE_INPUT_CONTRADICTION = object()


class ClearableFileInput(FileInput):
    clear_checkbox_label = _("Clear")
    initial_text = _("Currently")
    input_text = _("Change")
    template_name = "django/forms/widgets/clearable_file_input.html"
    checked = False

    def clear_checkbox_name(self, name):
        
        return name + "-clear"

    def clear_checkbox_id(self, name):
        
        return name + "_id"

    def is_initial(self, value):
        
        return bool(value and getattr(value, "url", False))

    def format_value(self, value):
        
        if self.is_initial(value):
            return value

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        checkbox_name = self.clear_checkbox_name(name)
        checkbox_id = self.clear_checkbox_id(checkbox_name)
        context["widget"].update(
            {
                "checkbox_name": checkbox_name,
                "checkbox_id": checkbox_id,
                "is_initial": self.is_initial(value),
                "input_text": self.input_text,
                "initial_text": self.initial_text,
                "clear_checkbox_label": self.clear_checkbox_label,
            }
        )
        context["widget"]["attrs"].setdefault("disabled", False)
        context["widget"]["attrs"]["checked"] = self.checked
        return context

    def value_from_datadict(self, data, files, name):
        upload = super().value_from_datadict(data, files, name)
        self.checked = self.clear_checkbox_name(name) in data
        if not self.is_required and CheckboxInput().value_from_datadict(
            data, files, self.clear_checkbox_name(name)
        ):
            if upload:
                
                
                
                return FILE_INPUT_CONTRADICTION
            
            return False
        return upload

    def value_omitted_from_data(self, data, files, name):
        return (
            super().value_omitted_from_data(data, files, name)
            and self.clear_checkbox_name(name) not in data
        )


class Textarea(Widget):
    template_name = "django/forms/widgets/textarea.html"

    def __init__(self, attrs=None):
        
        default_attrs = {"cols": "40", "rows": "10"}
        if attrs:
            default_attrs.update(attrs)
        super().__init__(default_attrs)


class DateTimeBaseInput(TextInput):
    format_key = ""
    supports_microseconds = False

    def __init__(self, attrs=None, format=None):
        super().__init__(attrs)
        self.format = format or None

    def format_value(self, value):
        return formats.localize_input(
            value, self.format or formats.get_format(self.format_key)[0]
        )


class DateInput(DateTimeBaseInput):
    format_key = "DATE_INPUT_FORMATS"
    template_name = "django/forms/widgets/date.html"


class DateTimeInput(DateTimeBaseInput):
    format_key = "DATETIME_INPUT_FORMATS"
    template_name = "django/forms/widgets/datetime.html"


class TimeInput(DateTimeBaseInput):
    format_key = "TIME_INPUT_FORMATS"
    template_name = "django/forms/widgets/time.html"



def boolean_check(v):
    return not (v is False or v is None or v == "")


class CheckboxInput(Input):
    input_type = "checkbox"
    template_name = "django/forms/widgets/checkbox.html"

    def __init__(self, attrs=None, check_test=None):
        super().__init__(attrs)
        
        
        self.check_test = boolean_check if check_test is None else check_test

    def format_value(self, value):
        
        if value is True or value is False or value is None or value == "":
            return
        return str(value)

    def get_context(self, name, value, attrs):
        if self.check_test(value):
            attrs = {**(attrs or {}), "checked": True}
        return super().get_context(name, value, attrs)

    def value_from_datadict(self, data, files, name):
        if name not in data:
            
            
            return False
        value = data.get(name)
        
        values = {"true": True, "false": False}
        if isinstance(value, str):
            value = values.get(value.lower(), value)
        return bool(value)

    def value_omitted_from_data(self, data, files, name):
        
        
        return False


class ChoiceWidget(Widget):
    allow_multiple_selected = False
    input_type = None
    template_name = None
    option_template_name = None
    add_id_index = True
    checked_attribute = {"checked": True}
    option_inherits_attrs = True

    def __init__(self, attrs=None, choices=()):
        super().__init__(attrs)
        self.choices = choices

    def __deepcopy__(self, memo):
        obj = copy.copy(self)
        obj.attrs = self.attrs.copy()
        obj.choices = copy.copy(self.choices)
        memo[id(self)] = obj
        return obj

    def subwidgets(self, name, value, attrs=None):
        
        value = self.format_value(value)
        yield from self.options(name, value, attrs)

    def options(self, name, value, attrs=None):
        
        for group in self.optgroups(name, value, attrs):
            yield from group[1]

    def optgroups(self, name, value, attrs=None):
        
        groups = []
        has_selected = False

        for index, (option_value, option_label) in enumerate(self.choices):
            if option_value is None:
                option_value = ""

            subgroup = []
            if isinstance(option_label, (list, tuple)):
                group_name = option_value
                subindex = 0
                choices = option_label
            else:
                group_name = None
                subindex = None
                choices = [(option_value, option_label)]
            groups.append((group_name, subgroup, index))

            for subvalue, sublabel in choices:
                selected = (not has_selected or self.allow_multiple_selected) and str(
                    subvalue
                ) in value
                has_selected |= selected
                subgroup.append(
                    self.create_option(
                        name,
                        subvalue,
                        sublabel,
                        selected,
                        index,
                        subindex=subindex,
                        attrs=attrs,
                    )
                )
                if subindex is not None:
                    subindex += 1
        return groups

    def create_option(
        self, name, value, label, selected, index, subindex=None, attrs=None
    ):
        index = str(index) if subindex is None else "%s_%s" % (index, subindex)
        option_attrs = (
            self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
        )
        if selected:
            option_attrs.update(self.checked_attribute)
        if "id" in option_attrs:
            option_attrs["id"] = self.id_for_label(option_attrs["id"], index)
        return {
            "name": name,
            "value": value,
            "label": label,
            "selected": selected,
            "index": index,
            "attrs": option_attrs,
            "type": self.input_type,
            "template_name": self.option_template_name,
            "wrap_label": True,
        }

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        context["widget"]["optgroups"] = self.optgroups(
            name, context["widget"]["value"], attrs
        )
        return context

    def id_for_label(self, id_, index="0"):
        
        if id_ and self.add_id_index:
            id_ = "%s_%s" % (id_, index)
        return id_

    def value_from_datadict(self, data, files, name):
        getter = data.get
        if self.allow_multiple_selected:
            try:
                getter = data.getlist
            except AttributeError:
                pass
        return getter(name)

    def format_value(self, value):
        
        if value is None and self.allow_multiple_selected:
            return []
        if not isinstance(value, (tuple, list)):
            value = [value]
        return [str(v) if v is not None else "" for v in value]

    @property
    def choices(self):
        return self._choices

    @choices.setter
    def choices(self, value):
        self._choices = normalize_choices(value)


class Select(ChoiceWidget):
    input_type = "select"
    template_name = "django/forms/widgets/select.html"
    option_template_name = "django/forms/widgets/select_option.html"
    add_id_index = False
    checked_attribute = {"selected": True}
    option_inherits_attrs = False

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        if self.allow_multiple_selected:
            context["widget"]["attrs"]["multiple"] = True
        return context

    @staticmethod
    def _choice_has_empty_value(choice):
        
        value, _ = choice
        return value is None or value == ""

    def use_required_attribute(self, initial):
        
        use_required_attribute = super().use_required_attribute(initial)
        
        if self.allow_multiple_selected:
            return use_required_attribute

        first_choice = next(iter(self.choices), None)
        return (
            use_required_attribute
            and first_choice is not None
            and self._choice_has_empty_value(first_choice)
        )


class NullBooleanSelect(Select):
    

    def __init__(self, attrs=None):
        choices = (
            ("unknown", _("Unknown")),
            ("true", _("Yes")),
            ("false", _("No")),
        )
        super().__init__(attrs, choices)

    def format_value(self, value):
        try:
            return {
                True: "true",
                False: "false",
                "true": "true",
                "false": "false",
                
                "2": "true",
                "3": "false",
            }[value]
        except KeyError:
            return "unknown"

    def value_from_datadict(self, data, files, name):
        value = data.get(name)
        return {
            True: True,
            "True": True,
            "False": False,
            False: False,
            "true": True,
            "false": False,
            
            "2": True,
            "3": False,
        }.get(value)


class SelectMultiple(Select):
    allow_multiple_selected = True

    def value_from_datadict(self, data, files, name):
        try:
            getter = data.getlist
        except AttributeError:
            getter = data.get
        return getter(name)

    def value_omitted_from_data(self, data, files, name):
        
        
        return False


class RadioSelect(ChoiceWidget):
    input_type = "radio"
    template_name = "django/forms/widgets/radio.html"
    option_template_name = "django/forms/widgets/radio_option.html"
    use_fieldset = True

    def id_for_label(self, id_, index=None):
        
        if index is None:
            return ""
        return super().id_for_label(id_, index)


class CheckboxSelectMultiple(RadioSelect):
    allow_multiple_selected = True
    input_type = "checkbox"
    template_name = "django/forms/widgets/checkbox_select.html"
    option_template_name = "django/forms/widgets/checkbox_option.html"

    def use_required_attribute(self, initial):
        
        
        return False

    def value_omitted_from_data(self, data, files, name):
        
        
        return False


class MultiWidget(Widget):
    

    template_name = "django/forms/widgets/multiwidget.html"
    use_fieldset = True

    def __init__(self, widgets, attrs=None):
        if isinstance(widgets, dict):
            self.widgets_names = [("_%s" % name) if name else "" for name in widgets]
            widgets = widgets.values()
        else:
            self.widgets_names = ["_%s" % i for i in range(len(widgets))]
        self.widgets = [w() if isinstance(w, type) else w for w in widgets]
        super().__init__(attrs)

    @property
    def is_hidden(self):
        return all(w.is_hidden for w in self.widgets)

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        if self.is_localized:
            for widget in self.widgets:
                widget.is_localized = self.is_localized
        
        
        if not isinstance(value, (list, tuple)):
            value = self.decompress(value)

        final_attrs = context["widget"]["attrs"]
        input_type = final_attrs.pop("type", None)
        id_ = final_attrs.get("id")
        subwidgets = []
        for i, (widget_name, widget) in enumerate(
            zip(self.widgets_names, self.widgets)
        ):
            if input_type is not None:
                widget.input_type = input_type
            widget_name = name + widget_name
            try:
                widget_value = value[i]
            except IndexError:
                widget_value = None
            if id_:
                widget_attrs = final_attrs.copy()
                widget_attrs["id"] = "%s_%s" % (id_, i)
            else:
                widget_attrs = final_attrs
            subwidgets.append(
                widget.get_context(widget_name, widget_value, widget_attrs)["widget"]
            )
        context["widget"]["subwidgets"] = subwidgets
        return context

    def id_for_label(self, id_):
        return ""

    def value_from_datadict(self, data, files, name):
        return [
            widget.value_from_datadict(data, files, name + widget_name)
            for widget_name, widget in zip(self.widgets_names, self.widgets)
        ]

    def value_omitted_from_data(self, data, files, name):
        return all(
            widget.value_omitted_from_data(data, files, name + widget_name)
            for widget_name, widget in zip(self.widgets_names, self.widgets)
        )

    def decompress(self, value):
        
        raise NotImplementedError("Subclasses must implement this method.")

    def _get_media(self):
        
        media = Media()
        for w in self.widgets:
            media += w.media
        return media

    media = property(_get_media)

    def __deepcopy__(self, memo):
        obj = super().__deepcopy__(memo)
        obj.widgets = copy.deepcopy(self.widgets)
        return obj

    @property
    def needs_multipart_form(self):
        return any(w.needs_multipart_form for w in self.widgets)


class SplitDateTimeWidget(MultiWidget):
    

    supports_microseconds = False
    template_name = "django/forms/widgets/splitdatetime.html"

    def __init__(
        self,
        attrs=None,
        date_format=None,
        time_format=None,
        date_attrs=None,
        time_attrs=None,
    ):
        widgets = (
            DateInput(
                attrs=attrs if date_attrs is None else date_attrs,
                format=date_format,
            ),
            TimeInput(
                attrs=attrs if time_attrs is None else time_attrs,
                format=time_format,
            ),
        )
        super().__init__(widgets)

    def decompress(self, value):
        if value:
            value = to_current_timezone(value)
            return [value.date(), value.time()]
        return [None, None]


class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
    

    template_name = "django/forms/widgets/splithiddendatetime.html"

    def __init__(
        self,
        attrs=None,
        date_format=None,
        time_format=None,
        date_attrs=None,
        time_attrs=None,
    ):
        super().__init__(attrs, date_format, time_format, date_attrs, time_attrs)
        for widget in self.widgets:
            widget.input_type = "hidden"


class SelectDateWidget(Widget):
    

    none_value = ("", "---")
    month_field = "%s_month"
    day_field = "%s_day"
    year_field = "%s_year"
    template_name = "django/forms/widgets/select_date.html"
    input_type = "select"
    select_widget = Select
    date_re = _lazy_re_compile(r"(\d{4}|0)-(\d\d?)-(\d\d?)$")
    use_fieldset = True

    def __init__(self, attrs=None, years=None, months=None, empty_label=None):
        self.attrs = attrs or {}

        
        if years:
            self.years = years
        else:
            this_year = datetime.date.today().year
            self.years = range(this_year, this_year + 10)

        
        if months:
            self.months = months
        else:
            self.months = MONTHS

        
        if isinstance(empty_label, (list, tuple)):
            if not len(empty_label) == 3:
                raise ValueError("empty_label list/tuple must have 3 elements.")

            self.year_none_value = ("", empty_label[0])
            self.month_none_value = ("", empty_label[1])
            self.day_none_value = ("", empty_label[2])
        else:
            if empty_label is not None:
                self.none_value = ("", empty_label)

            self.year_none_value = self.none_value
            self.month_none_value = self.none_value
            self.day_none_value = self.none_value

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        date_context = {}
        year_choices = [(i, str(i)) for i in self.years]
        if not self.is_required:
            year_choices.insert(0, self.year_none_value)
        year_name = self.year_field % name
        date_context["year"] = self.select_widget(
            attrs, choices=year_choices
        ).get_context(
            name=year_name,
            value=context["widget"]["value"]["year"],
            attrs={**context["widget"]["attrs"], "id": "id_%s" % year_name},
        )
        month_choices = list(self.months.items())
        if not self.is_required:
            month_choices.insert(0, self.month_none_value)
        month_name = self.month_field % name
        date_context["month"] = self.select_widget(
            attrs, choices=month_choices
        ).get_context(
            name=month_name,
            value=context["widget"]["value"]["month"],
            attrs={**context["widget"]["attrs"], "id": "id_%s" % month_name},
        )
        day_choices = [(i, i) for i in range(1, 32)]
        if not self.is_required:
            day_choices.insert(0, self.day_none_value)
        day_name = self.day_field % name
        date_context["day"] = self.select_widget(
            attrs,
            choices=day_choices,
        ).get_context(
            name=day_name,
            value=context["widget"]["value"]["day"],
            attrs={**context["widget"]["attrs"], "id": "id_%s" % day_name},
        )
        subwidgets = []
        for field in self._parse_date_fmt():
            subwidgets.append(date_context[field]["widget"])
        context["widget"]["subwidgets"] = subwidgets
        return context

    def format_value(self, value):
        
        year, month, day = None, None, None
        if isinstance(value, (datetime.date, datetime.datetime)):
            year, month, day = value.year, value.month, value.day
        elif isinstance(value, str):
            match = self.date_re.match(value)
            if match:
                
                
                year, month, day = [int(val) or "" for val in match.groups()]
            else:
                input_format = get_format("DATE_INPUT_FORMATS")[0]
                try:
                    d = datetime.datetime.strptime(value, input_format)
                except ValueError:
                    pass
                else:
                    year, month, day = d.year, d.month, d.day
        return {"year": year, "month": month, "day": day}

    @staticmethod
    def _parse_date_fmt():
        fmt = get_format("DATE_FORMAT")
        escaped = False
        for char in fmt:
            if escaped:
                escaped = False
            elif char == "\\":
                escaped = True
            elif char in "Yy":
                yield "year"
            elif char in "bEFMmNn":
                yield "month"
            elif char in "dj":
                yield "day"

    def id_for_label(self, id_):
        for first_select in self._parse_date_fmt():
            return "%s_%s" % (id_, first_select)
        return "%s_month" % id_

    def value_from_datadict(self, data, files, name):
        y = data.get(self.year_field % name)
        m = data.get(self.month_field % name)
        d = data.get(self.day_field % name)
        if y == m == d == "":
            return None
        if y is not None and m is not None and d is not None:
            input_format = get_format("DATE_INPUT_FORMATS")[0]
            input_format = formats.sanitize_strftime_format(input_format)
            try:
                date_value = datetime.date(int(y), int(m), int(d))
            except ValueError:
                
                
                return "%s-%s-%s" % (y or 0, m or 0, d or 0)
            except OverflowError:
                return "0-0-0"
            return date_value.strftime(input_format)
        return data.get(name)

    def value_omitted_from_data(self, data, files, name):
        return not any(
            ("{}_{}".format(name, interval) in data)
            for interval in ("year", "month", "day")
        )