import re
from warnings import warn
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl", "lrq3000"]}
__all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange']
tqdm = tqdm_gui
trange = tgrange

class tqdm_gui(std_tqdm):  # pragma: no cover

    def __init__(self, *args, **kwargs):
        from collections import deque

        import matplotlib as mpl
        import matplotlib.pyplot as plt
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        colour = kwargs.pop('colour', 'g')
        super(tqdm_gui, self).__init__(*args, **kwargs)

        if self.disable:
            return

        warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        self.mpl = mpl
        self.plt = plt

        # Remember if external environment uses toolbars
        self.toolbar = self.mpl.rcParams['toolbar']
        self.mpl.rcParams['toolbar'] = 'None'

        self.mininterval = max(self.mininterval, 0.5)
        self.fig, ax = plt.subplots(figsize=(9, 2.2))
        # self.fig.subplots_adjust(bottom=0.2)
        total = self.__len__()  # avoids TypeError on None #971
        if total is not None:
            self.xdata = []
            self.ydata = []
            self.zdata = []
        else:
            self.xdata = deque([])
            self.ydata = deque([])
            self.zdata = deque([])
        self.line1, = ax.plot(self.xdata, self.ydata, color='b')
        self.line2, = ax.plot(self.xdata, self.zdata, color='k')
        ax.set_ylim(0, 0.001)
        if total is not None:
            ax.set_xlim(0, 100)
            ax.set_xlabel("percent")
            self.fig.legend((self.line1, self.line2), ("cur", "est"),
                            loc='center right')
            # progressbar
            self.hspan = plt.axhspan(0, 0.001, xmin=0, xmax=0, color=colour)
        else:
            # ax.set_xlim(-60, 0)
            ax.set_xlim(0, 60)
            ax.invert_xaxis()
            ax.set_xlabel("seconds")
            ax.legend(("cur", "est"), loc='lower left')
        ax.grid()
        # ax.set_xlabel('seconds')
        ax.set_ylabel((self.unit if self.unit else "it") + "/s")
        if self.unit_scale:
            plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
            ax.yaxis.get_offset_text().set_x(-0.15)

        # Remember if external environment is interactive
        self.wasion = plt.isinteractive()
        plt.ion()
        self.ax = ax

    def display(self, *_, **__):
        n = self.n
        cur_t = self._time()
        elapsed = cur_t - self.start_t
        delta_it = n - self.last_print_n
        delta_t = cur_t - self.last_print_t

        # Inline due to multiple calls
        total = self.total
        xdata = self.xdata
        ydata = self.ydata
        zdata = self.zdata
        ax = self.ax
        line1 = self.line1
        line2 = self.line2
        # instantaneous rate
        y = delta_it / delta_t
        # overall rate
        z = n / elapsed
        # update line data
        xdata.append(n * 100.0 / total if total else cur_t)
        ydata.append(y)
        zdata.append(z)

        # Discard old values
        # xmin, xmax = ax.get_xlim()
        # if (not total) and elapsed > xmin * 1.1:
        if (not total) and elapsed > 66:
            xdata.popleft()
            ydata.popleft()
            zdata.popleft()

        ymin, ymax = ax.get_ylim()
        if y > ymax or z > ymax:
            ymax = 1.1 * y
            ax.set_ylim(ymin, ymax)
            ax.figure.canvas.draw()

        if total:
            line1.set_data(xdata, ydata)
            line2.set_data(xdata, zdata)
            try:
                poly_lims = self.hspan.get_xy()
            except AttributeError:
                self.hspan = self.plt.axhspan(0, 0.001, xmin=0, xmax=0, color='g')
                poly_lims = self.hspan.get_xy()
            poly_lims[0, 1] = ymin
            poly_lims[1, 1] = ymax
            poly_lims[2] = [n / total, ymax]
            poly_lims[3] = [poly_lims[2, 0], ymin]
            if len(poly_lims) > 4:
                poly_lims[4, 1] = ymin
            self.hspan.set_xy(poly_lims)
        else:
            t_ago = [cur_t - i for i in xdata]
            line1.set_data(t_ago, ydata)
            line2.set_data(t_ago, zdata)

        d = self.format_dict
        # remove {bar}
        d['bar_format'] = (d['bar_format'] or "{l_bar}<bar/>{r_bar}").replace(
            "{bar}", "<bar/>")
        msg = self.format_meter(**d)
        if '<bar/>' in msg:
            msg = "".join(re.split(r'\|?<bar/>\|?', msg, 1))
        ax.set_title(msg, fontname="DejaVu Sans Mono", fontsize=11)
        self.plt.pause(1e-9)


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class TqdmHBox(HBox):

    def __repr__(self, pretty=False):
        pbar = getattr(self, 'pbar', None)
        if pbar is None:
            return super(TqdmHBox, self).__repr__()
        return pbar.format_meter(**self._repr_json_(pretty))


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    @staticmethod
    def status_printer(_, total=None, desc=None, ncols=None):
        """
        Manage the printing of an IPython/Jupyter Notebook progress bar widget.
        """
        # Fallback to text bar if there's no total
        # DEPRECATED: replaced with an 'info' style bar
        # if not total:
        #    return super(tqdm_notebook, tqdm_notebook).status_printer(file)

        # fp = file

        # Prepare IPython progress bar
        if IProgress is None:  # #187 #451 #558 #872
            raise ImportError(
                "IProgress not found. Please update jupyter and ipywidgets."
                " See https://ipywidgets.readthedocs.io/en/stable"
                "/user_install.html")
        if total:
            pbar = IProgress(min=0, max=total)
        else:  # No total? Show info style bar with no progress tqdm status
            pbar = IProgress(min=0, max=1)
            pbar.value = 1
            pbar.bar_style = 'info'
            if ncols is None:
                pbar.layout.width = "20px"

        ltext = HTML()
        rtext = HTML()
        if desc:
            ltext.value = desc
        container = TqdmHBox(children=[ltext, pbar, rtext])
        # Prepare layout
        if ncols is not None:  # use default style of ipywidgets
            # ncols could be 100, "100px", "100%"
            ncols = str(ncols)  # ipywidgets only accepts string
            try:
                if int(ncols) > 0:  # isnumeric and positive
                    ncols += 'px'
            except ValueError:
                pass
            pbar.layout.flex = '2'
            container.layout.width = ncols
            container.layout.display = 'inline-flex'
            container.layout.flex_flow = 'row wrap'

        return container


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    def display(self, msg=None, pos=None,
                # additional signals
                close=False, bar_style=None, check_delay=True):
        # Note: contrary to native tqdm, msg='' does NOT clear bar
        # goal is to keep all infos if error happens so user knows
        # at which iteration the loop failed.

        # Clear previous output (really necessary?)
        # clear_output(wait=1)

        if not msg and not close:
            d = self.format_dict
            # remove {bar}
            d['bar_format'] = (d['bar_format'] or "{l_bar}<bar/>{r_bar}").replace(
                "{bar}", "<bar/>")
            msg = self.format_meter(**d)

        ltext, pbar, rtext = self.container.children
        pbar.value = self.n

        if msg:
            # html escape special characters (like '&')
            if '<bar/>' in msg:
                left, right = map(escape, re.split(r'\|?<bar/>\|?', msg, 1))
            else:
                left, right = '', escape(msg)

            # Update description
            ltext.value = left
            # never clear the bar (signal: msg='')
            if right:
                rtext.value = right

        # Change bar style
        if bar_style:
            # Hack-ish way to avoid the danger bar_style being overridden by
            # success because the bar gets closed after the error...
            if pbar.bar_style != 'danger' or bar_style != 'success':
                pbar.bar_style = bar_style

        # Special signal to close the bar
        if close and pbar.bar_style != 'danger':  # hide only if no error
            try:
                self.container.close()
            except AttributeError:
                self.container.visible = False

        if check_delay and self.delay > 0 and not self.displayed:
            display(self.container)
            self.displayed = True


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    def __iter__(self):
        try:
            for obj in super(tqdm_notebook, self).__iter__():
                # return super(tqdm...) will not catch exception
                yield obj
        # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt
        except:  # NOQA
            self.disp(bar_style='danger')
            raise
        # NB: don't `finally: close()`
        # since this could be a shared bar which the user will `reset()`


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    def update(self, n=1):
        try:
            return super(tqdm_notebook, self).update(n=n)
        # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt
        except:  # NOQA
            # cannot catch KeyboardInterrupt when using manual tqdm
            # as the interrupt will most likely happen on another statement
            self.disp(bar_style='danger')
            raise
        # NB: don't `finally: close()`
        # since this could be a shared bar which the user will `reset()`


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    def close(self):
        if self.disable:
            return
        super(tqdm_notebook, self).close()
        # Try to detect if there was an error or KeyboardInterrupt
        # in manual mode: if n < total, things probably got wrong
        if self.total and self.n < self.total:
            self.disp(bar_style='danger', check_delay=False)
        else:
            if self.leave:
                self.disp(bar_style='success', check_delay=False)
            else:
                self.disp(close=True, check_delay=False)


    def clear(self, *_, **__):
        pass

import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    def clear(self, *_, **__):
        pass


import re
import sys
from weakref import proxy
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]}
__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange']
tqdm = tqdm_notebook
trange = tnrange

class tqdm_notebook(std_tqdm):

    def __init__(self, *args, **kwargs):
        """
        Supports the usual `tqdm.tqdm` parameters as well as those listed below.

        Parameters
        ----------
        display  : Whether to call `display(self.container)` immediately
            [default: True].
        """
        kwargs = kwargs.copy()
        # Setup default output
        file_kwarg = kwargs.get('file', sys.stderr)
        if file_kwarg is sys.stderr or file_kwarg is None:
            kwargs['file'] = sys.stdout  # avoid the red block in IPython

        # Initialize parent class + avoid printing by using gui=True
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        colour = kwargs.pop('colour', None)
        display_here = kwargs.pop('display', True)
        super(tqdm_notebook, self).__init__(*args, **kwargs)
        if self.disable or not kwargs['gui']:
            self.disp = lambda *_, **__: None
            return

        # Get bar width
        self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None)

        # Replace with IPython progress bar display (with correct total)
        unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
        total = self.total * unit_scale if self.total else self.total
        self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
        self.container.pbar = proxy(self)
        self.displayed = False
        if display_here and self.delay <= 0:
            display(self.container)
            self.displayed = True
        self.disp = self.display
        self.colour = colour

        # Print initial bar state
        if not self.disable:
            self.display(check_delay=False)

    def reset(self, total=None):
        """
        Resets to 0 iterations for repeated use.

        Consider combining with `leave=True`.

        Parameters
        ----------
        total  : int or float, optional. Total to use for the new bar.
        """
        if self.disable:
            return super(tqdm_notebook, self).reset(total=total)
        _, pbar, _ = self.container.children
        pbar.bar_style = ''
        if total is not None:
            pbar.max = total
            if not self.total and self.ncols is None:  # no longer unknown total
                pbar.layout.width = None  # reset width
        return super(tqdm_notebook, self).reset(total=total)


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class FractionColumn(ProgressColumn):

    def __init__(self, unit_scale=False, unit_divisor=1000):
        self.unit_scale = unit_scale
        self.unit_divisor = unit_divisor
        super().__init__()

    def render(self, task):
        """Calculate common unit for completed and total."""
        completed = int(task.completed)
        total = int(task.total)
        if self.unit_scale:
            unit, suffix = filesize.pick_unit_and_suffix(
                total,
                ["", "K", "M", "G", "T", "P", "E", "Z", "Y"],
                self.unit_divisor,
            )
        else:
            unit, suffix = filesize.pick_unit_and_suffix(total, [""], 1)
        precision = 0 if unit == 1 else 1
        return Text(
            f"{completed/unit:,.{precision}f}/{total/unit:,.{precision}f} {suffix}",
            style="progress.download")


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class RateColumn(ProgressColumn):

    def __init__(self, unit="", unit_scale=False, unit_divisor=1000):
        self.unit = unit
        self.unit_scale = unit_scale
        self.unit_divisor = unit_divisor
        super().__init__()

    def render(self, task):
        """Show data transfer speed."""
        speed = task.speed
        if speed is None:
            return Text(f"? {self.unit}/s", style="progress.data.speed")
        if self.unit_scale:
            unit, suffix = filesize.pick_unit_and_suffix(
                speed,
                ["", "K", "M", "G", "T", "P", "E", "Z", "Y"],
                self.unit_divisor,
            )
        else:
            unit, suffix = filesize.pick_unit_and_suffix(speed, [""], 1)
        precision = 0 if unit == 1 else 1
        return Text(f"{speed/unit:,.{precision}f} {suffix}{self.unit}/s",
                    style="progress.data.speed")


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class FractionColumn(ProgressColumn):

    def __init__(self, unit_scale=False, unit_divisor=1000):
        self.unit_scale = unit_scale
        self.unit_divisor = unit_divisor
        super().__init__()

class RateColumn(ProgressColumn):

    def __init__(self, unit="", unit_scale=False, unit_divisor=1000):
        self.unit = unit
        self.unit_scale = unit_scale
        self.unit_divisor = unit_divisor
        super().__init__()

class tqdm_rich(std_tqdm):  # pragma: no cover

    def __init__(self, *args, **kwargs):
        """
        This class accepts the following parameters *in addition* to
        the parameters accepted by `tqdm`.

        Parameters
        ----------
        progress  : tuple, optional
            arguments for `rich.progress.Progress()`.
        """
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        progress = kwargs.pop('progress', None)
        super(tqdm_rich, self).__init__(*args, **kwargs)

        if self.disable:
            return

        warn("rich is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        d = self.format_dict
        if progress is None:
            progress = (
                "[progress.description]{task.description}"
                "[progress.percentage]{task.percentage:>4.0f}%",
                BarColumn(bar_width=None),
                FractionColumn(
                    unit_scale=d['unit_scale'], unit_divisor=d['unit_divisor']),
                "[", TimeElapsedColumn(), "<", TimeRemainingColumn(),
                ",", RateColumn(unit=d['unit'], unit_scale=d['unit_scale'],
                                unit_divisor=d['unit_divisor']), "]"
            )
        self._prog = Progress(*progress, transient=not self.leave)
        self._prog.__enter__()
        self._task_id = self._prog.add_task(self.desc or "", **d)


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class tqdm_rich(std_tqdm):  # pragma: no cover

    def __init__(self, *args, **kwargs):
        """
        This class accepts the following parameters *in addition* to
        the parameters accepted by `tqdm`.

        Parameters
        ----------
        progress  : tuple, optional
            arguments for `rich.progress.Progress()`.
        """
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        progress = kwargs.pop('progress', None)
        super(tqdm_rich, self).__init__(*args, **kwargs)

        if self.disable:
            return

        warn("rich is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        d = self.format_dict
        if progress is None:
            progress = (
                "[progress.description]{task.description}"
                "[progress.percentage]{task.percentage:>4.0f}%",
                BarColumn(bar_width=None),
                FractionColumn(
                    unit_scale=d['unit_scale'], unit_divisor=d['unit_divisor']),
                "[", TimeElapsedColumn(), "<", TimeRemainingColumn(),
                ",", RateColumn(unit=d['unit'], unit_scale=d['unit_scale'],
                                unit_divisor=d['unit_divisor']), "]"
            )
        self._prog = Progress(*progress, transient=not self.leave)
        self._prog.__enter__()
        self._task_id = self._prog.add_task(self.desc or "", **d)

    def close(self):
        if self.disable:
            return
        super(tqdm_rich, self).close()
        self._prog.__exit__(None, None, None)


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class tqdm_rich(std_tqdm):  # pragma: no cover

    def __init__(self, *args, **kwargs):
        """
        This class accepts the following parameters *in addition* to
        the parameters accepted by `tqdm`.

        Parameters
        ----------
        progress  : tuple, optional
            arguments for `rich.progress.Progress()`.
        """
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        progress = kwargs.pop('progress', None)
        super(tqdm_rich, self).__init__(*args, **kwargs)

        if self.disable:
            return

        warn("rich is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        d = self.format_dict
        if progress is None:
            progress = (
                "[progress.description]{task.description}"
                "[progress.percentage]{task.percentage:>4.0f}%",
                BarColumn(bar_width=None),
                FractionColumn(
                    unit_scale=d['unit_scale'], unit_divisor=d['unit_divisor']),
                "[", TimeElapsedColumn(), "<", TimeRemainingColumn(),
                ",", RateColumn(unit=d['unit'], unit_scale=d['unit_scale'],
                                unit_divisor=d['unit_divisor']), "]"
            )
        self._prog = Progress(*progress, transient=not self.leave)
        self._prog.__enter__()
        self._task_id = self._prog.add_task(self.desc or "", **d)

    def clear(self, *_, **__):
        pass


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class tqdm_rich(std_tqdm):  # pragma: no cover

    def __init__(self, *args, **kwargs):
        """
        This class accepts the following parameters *in addition* to
        the parameters accepted by `tqdm`.

        Parameters
        ----------
        progress  : tuple, optional
            arguments for `rich.progress.Progress()`.
        """
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        progress = kwargs.pop('progress', None)
        super(tqdm_rich, self).__init__(*args, **kwargs)

        if self.disable:
            return

        warn("rich is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        d = self.format_dict
        if progress is None:
            progress = (
                "[progress.description]{task.description}"
                "[progress.percentage]{task.percentage:>4.0f}%",
                BarColumn(bar_width=None),
                FractionColumn(
                    unit_scale=d['unit_scale'], unit_divisor=d['unit_divisor']),
                "[", TimeElapsedColumn(), "<", TimeRemainingColumn(),
                ",", RateColumn(unit=d['unit'], unit_scale=d['unit_scale'],
                                unit_divisor=d['unit_divisor']), "]"
            )
        self._prog = Progress(*progress, transient=not self.leave)
        self._prog.__enter__()
        self._task_id = self._prog.add_task(self.desc or "", **d)

    def display(self, *_, **__):
        if not hasattr(self, '_prog'):
            return
        self._prog.update(self._task_id, completed=self.n, description=self.desc)


from warnings import warn
from rich.progress import (
    BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize)
from .std import TqdmExperimentalWarning
from .std import tqdm as std_tqdm
from .utils import _range

__author__ = {"github.com/": ["casperdcl"]}
__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange']
tqdm = tqdm_rich
trange = trrange

class tqdm_rich(std_tqdm):  # pragma: no cover

    def __init__(self, *args, **kwargs):
        """
        This class accepts the following parameters *in addition* to
        the parameters accepted by `tqdm`.

        Parameters
        ----------
        progress  : tuple, optional
            arguments for `rich.progress.Progress()`.
        """
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        progress = kwargs.pop('progress', None)
        super(tqdm_rich, self).__init__(*args, **kwargs)

        if self.disable:
            return

        warn("rich is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        d = self.format_dict
        if progress is None:
            progress = (
                "[progress.description]{task.description}"
                "[progress.percentage]{task.percentage:>4.0f}%",
                BarColumn(bar_width=None),
                FractionColumn(
                    unit_scale=d['unit_scale'], unit_divisor=d['unit_divisor']),
                "[", TimeElapsedColumn(), "<", TimeRemainingColumn(),
                ",", RateColumn(unit=d['unit'], unit_scale=d['unit_scale'],
                                unit_divisor=d['unit_divisor']), "]"
            )
        self._prog = Progress(*progress, transient=not self.leave)
        self._prog.__enter__()
        self._task_id = self._prog.add_task(self.desc or "", **d)

    def reset(self, total=None):
        """
        Resets to 0 iterations for repeated use.

        Parameters
        ----------
        total  : int or float, optional. Total to use for the new bar.
        """
        if hasattr(self, '_prog'):
            self._prog.reset(total=total)
        super(tqdm_rich, self).reset(total=total)


import typing
from collections.abc import Mapping

class Position:

    def __init__(self, line_no: int, column_no: int, char_index: int):
        self.line_no = line_no
        self.column_no = column_no
        self.char_index = char_index

    def __eq__(self, other: typing.Any) -> bool:
        return (
            isinstance(other, Position)
            and self.line_no == other.line_no
            and self.column_no == other.column_no
            and self.char_index == other.char_index
        )


import typing
from collections.abc import Mapping

class Message:

    def __init__(
        self,
        *,
        text: str,
        code: str = None,
        key: typing.Union[int, str] = None,
        index: typing.List[typing.Union[int, str]] = None,
        position: Position = None,
        start_position: Position = None,
        end_position: Position = None,
    ):
        """
        text - The error message. 'May not have more than 100 characters'
        code - An optional error code, eg. 'max_length'
        key - An optional key of the message within a single parent. eg. 'username'
        index - The index of the message within a nested object. eg. ['users', 3, 'username']

        Optionally either:

        position - The start and end position of the error message within the raw content.

        Or:

        start_position - The start position of the error message within the raw content.
        end_position - The end position of the error message within the raw content.
        """
        self.text = text
        self.code = "custom" if code is None else code
        if key is not None:
            assert index is None
            self.index = [key]
        else:
            self.index = [] if index is None else index

        if position is None:
            self.start_position = start_position
            self.end_position = end_position
        else:
            assert start_position is None
            assert end_position is None
            self.start_position = position
            self.end_position = position

    def __eq__(self, other: typing.Any) -> bool:
        return isinstance(other, Message) and (
            self.text == other.text
            and self.code == other.code
            and self.index == other.index
            and self.start_position == other.start_position
            and self.end_position == other.end_position
        )


import typing
from collections.abc import Mapping

class Position:

    def __init__(self, line_no: int, column_no: int, char_index: int):
        self.line_no = line_no
        self.column_no = column_no
        self.char_index = char_index

class Message:

    def __init__(
        self,
        *,
        text: str,
        code: str = None,
        key: typing.Union[int, str] = None,
        index: typing.List[typing.Union[int, str]] = None,
        position: Position = None,
        start_position: Position = None,
        end_position: Position = None,
    ):
        """
        text - The error message. 'May not have more than 100 characters'
        code - An optional error code, eg. 'max_length'
        key - An optional key of the message within a single parent. eg. 'username'
        index - The index of the message within a nested object. eg. ['users', 3, 'username']

        Optionally either:

        position - The start and end position of the error message within the raw content.

        Or:

        start_position - The start position of the error message within the raw content.
        end_position - The end position of the error message within the raw content.
        """
        self.text = text
        self.code = "custom" if code is None else code
        if key is not None:
            assert index is None
            self.index = [key]
        else:
            self.index = [] if index is None else index

        if position is None:
            self.start_position = start_position
            self.end_position = end_position
        else:
            assert start_position is None
            assert end_position is None
            self.start_position = position
            self.end_position = position

class BaseError(Mapping, Exception):

    def __init__(
        self,
        *,
        text: str = None,
        code: str = None,
        key: typing.Union[int, str] = None,
        position: Position = None,
        messages: typing.List[Message] = None,
    ):
        """
        Either instantiated with a single message, like so:

        text - The error message. 'May not have more than 100 characters'
        code - An optional error code, eg. 'max_length'
        key - An optional key of the message within a single parent. eg. 'username'

        Or instantiated with a list of error messages:

        messages - A list of all the messages in the error.
        """
        if messages is None:
            # Instantiated as a ValidationError with a single error message.
            assert text is not None
            messages = [Message(text=text, code=code, key=key, position=position)]
        else:
            # Instantiated as a ValidationError with multiple error messages.
            assert text is None
            assert code is None
            assert key is None
            assert position is None
            assert len(messages)

        self._messages = messages
        self._message_dict: typing.Dict[
            typing.Union[int, str], typing.Union[str, dict]
        ] = {}

        # Populate 'self._message_dict'
        for message in messages:
            insert_into = self._message_dict
            for key in message.index[:-1]:
                insert_into = insert_into.setdefault(key, {})  # type: ignore
            insert_key = message.index[-1] if message.index else ""
            insert_into[insert_key] = message.text


import typing
from collections.abc import Mapping

class BaseError(Mapping, Exception):

    def __init__(
        self,
        *,
        text: str = None,
        code: str = None,
        key: typing.Union[int, str] = None,
        position: Position = None,
        messages: typing.List[Message] = None,
    ):
        """
        Either instantiated with a single message, like so:

        text - The error message. 'May not have more than 100 characters'
        code - An optional error code, eg. 'max_length'
        key - An optional key of the message within a single parent. eg. 'username'

        Or instantiated with a list of error messages:

        messages - A list of all the messages in the error.
        """
        if messages is None:
            # Instantiated as a ValidationError with a single error message.
            assert text is not None
            messages = [Message(text=text, code=code, key=key, position=position)]
        else:
            # Instantiated as a ValidationError with multiple error messages.
            assert text is None
            assert code is None
            assert key is None
            assert position is None
            assert len(messages)

        self._messages = messages
        self._message_dict: typing.Dict[
            typing.Union[int, str], typing.Union[str, dict]
        ] = {}

        # Populate 'self._message_dict'
        for message in messages:
            insert_into = self._message_dict
            for key in message.index[:-1]:
                insert_into = insert_into.setdefault(key, {})  # type: ignore
            insert_key = message.index[-1] if message.index else ""
            insert_into[insert_key] = message.text

    def __eq__(self, other: typing.Any) -> bool:
        return isinstance(other, ValidationError) and self._messages == other._messages


import typing
from collections.abc import Mapping

class BaseError(Mapping, Exception):

    def __init__(
        self,
        *,
        text: str = None,
        code: str = None,
        key: typing.Union[int, str] = None,
        position: Position = None,
        messages: typing.List[Message] = None,
    ):
        """
        Either instantiated with a single message, like so:

        text - The error message. 'May not have more than 100 characters'
        code - An optional error code, eg. 'max_length'
        key - An optional key of the message within a single parent. eg. 'username'

        Or instantiated with a list of error messages:

        messages - A list of all the messages in the error.
        """
        if messages is None:
            # Instantiated as a ValidationError with a single error message.
            assert text is not None
            messages = [Message(text=text, code=code, key=key, position=position)]
        else:
            # Instantiated as a ValidationError with multiple error messages.
            assert text is None
            assert code is None
            assert key is None
            assert position is None
            assert len(messages)

        self._messages = messages
        self._message_dict: typing.Dict[
            typing.Union[int, str], typing.Union[str, dict]
        ] = {}

        # Populate 'self._message_dict'
        for message in messages:
            insert_into = self._message_dict
            for key in message.index[:-1]:
                insert_into = insert_into.setdefault(key, {})  # type: ignore
            insert_key = message.index[-1] if message.index else ""
            insert_into[insert_key] = message.text

    def __repr__(self) -> str:
        class_name = self.__class__.__name__
        if len(self._messages) == 1 and not self._messages[0].index:
            message = self._messages[0]
            return f"{class_name}(text={message.text!r}, code={message.code!r})"
        return f"{class_name}({self._messages!r})"


import typing
from collections.abc import Mapping

class BaseError(Mapping, Exception):

    def __init__(
        self,
        *,
        text: str = None,
        code: str = None,
        key: typing.Union[int, str] = None,
        position: Position = None,
        messages: typing.List[Message] = None,
    ):
        """
        Either instantiated with a single message, like so:

        text - The error message. 'May not have more than 100 characters'
        code - An optional error code, eg. 'max_length'
        key - An optional key of the message within a single parent. eg. 'username'

        Or instantiated with a list of error messages:

        messages - A list of all the messages in the error.
        """
        if messages is None:
            # Instantiated as a ValidationError with a single error message.
            assert text is not None
            messages = [Message(text=text, code=code, key=key, position=position)]
        else:
            # Instantiated as a ValidationError with multiple error messages.
            assert text is None
            assert code is None
            assert key is None
            assert position is None
            assert len(messages)

        self._messages = messages
        self._message_dict: typing.Dict[
            typing.Union[int, str], typing.Union[str, dict]
        ] = {}

        # Populate 'self._message_dict'
        for message in messages:
            insert_into = self._message_dict
            for key in message.index[:-1]:
                insert_into = insert_into.setdefault(key, {})  # type: ignore
            insert_key = message.index[-1] if message.index else ""
            insert_into[insert_key] = message.text

    def __str__(self) -> str:
        if len(self._messages) == 1 and not self._messages[0].index:
            return self._messages[0].text
        return str(dict(self))


import typing
from collections.abc import Mapping

class ValidationResult:

    def __init__(
        self, *, value: typing.Any = None, error: ValidationError = None
    ) -> None:
        """
        Either:

        value - The validated data.

        Or:

        error - The validation error.
        """
        assert value is None or error is None
        self.value = value
        self.error = error

    def __iter__(self) -> typing.Iterator:
        yield self.value
        yield self.error


import typing
from typesystem.fields import Any, Field

class NeverMatch(Field):

    errors = {"never": "This never validates."}

    def __init__(self, **kwargs: typing.Any) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)


import typing
from typesystem.fields import Any, Field

class OneOf(Field):

    errors = {
        "no_match": "Did not match any valid type.",
        "multiple_matches": "Matched more than one type.",
    }

    def __init__(self, one_of: typing.List[Field], **kwargs: typing.Any) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.one_of = one_of


import typing
from typesystem.fields import Any, Field

class OneOf(Field):

    errors = {
        "no_match": "Did not match any valid type.",
        "multiple_matches": "Matched more than one type.",
    }

    def __init__(self, one_of: typing.List[Field], **kwargs: typing.Any) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.one_of = one_of

    def validate(self, value: typing.Any, strict: bool = False) -> typing.Any:
        candidate = None
        match_count = 0
        for child in self.one_of:
            validated, error = child.validate_or_error(value, strict=strict)
            if error is None:
                match_count += 1
                candidate = validated

        if match_count == 1:
            return candidate
        elif match_count > 1:
            raise self.validation_error("multiple_matches")
        raise self.validation_error("no_match")


import typing
from typesystem.fields import Any, Field

class AllOf(Field):

    def __init__(self, all_of: typing.List[Field], **kwargs: typing.Any) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.all_of = all_of


import typing
from typesystem.fields import Any, Field

class Not(Field):

    errors = {"negated": "Must not match."}

    def __init__(self, negated: Field, **kwargs: typing.Any) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.negated = negated


import typing
from typesystem.fields import Any, Field

class Not(Field):

    errors = {"negated": "Must not match."}

    def __init__(self, negated: Field, **kwargs: typing.Any) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.negated = negated

    def validate(self, value: typing.Any, strict: bool = False) -> typing.Any:
        _, error = self.negated.validate_or_error(value, strict=strict)
        if error:
            return value
        raise self.validation_error("negated")


import typing
from typesystem.fields import Any, Field

class IfThenElse(Field):

    def __init__(
        self,
        if_clause: Field,
        then_clause: Field = None,
        else_clause: Field = None,
        **kwargs: typing.Any
    ) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.if_clause = if_clause
        self.then_clause = Any() if then_clause is None else then_clause
        self.else_clause = Any() if else_clause is None else else_clause


import typing
from typesystem.fields import Any, Field

class IfThenElse(Field):

    def __init__(
        self,
        if_clause: Field,
        then_clause: Field = None,
        else_clause: Field = None,
        **kwargs: typing.Any
    ) -> None:
        assert "allow_null" not in kwargs
        super().__init__(**kwargs)
        self.if_clause = if_clause
        self.then_clause = Any() if then_clause is None else then_clause
        self.else_clause = Any() if else_clause is None else else_clause

    def validate(self, value: typing.Any, strict: bool = False) -> typing.Any:
        _, error = self.if_clause.validate_or_error(value, strict=strict)
        if error is None:
            return self.then_clause.validate(value, strict=strict)
        else:
            return self.else_clause.validate(value, strict=strict)


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Field:

    errors: typing.Dict[str, str] = {}

    _creation_counter = 0

    def __init__(
        self,
        *,
        title: str = "",
        description: str = "",
        default: typing.Any = NO_DEFAULT,
        allow_null: bool = False,
    ):
        assert isinstance(title, str)
        assert isinstance(description, str)

        if allow_null and default is NO_DEFAULT:
            default = None

        if default is not NO_DEFAULT:
            self.default = default

        self.title = title
        self.description = description
        self.allow_null = allow_null

        # We need this global counter to determine what order fields have
        # been declared in when used with `Schema`.
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

    def validate_or_error(
        self, value: typing.Any, *, strict: bool = False
    ) -> ValidationResult:
        try:
            value = self.validate(value, strict=strict)
        except ValidationError as error:
            return ValidationResult(value=None, error=error)
        return ValidationResult(value=value, error=None)


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Field:

    errors: typing.Dict[str, str] = {}

    _creation_counter = 0

    def __init__(
        self,
        *,
        title: str = "",
        description: str = "",
        default: typing.Any = NO_DEFAULT,
        allow_null: bool = False,
    ):
        assert isinstance(title, str)
        assert isinstance(description, str)

        if allow_null and default is NO_DEFAULT:
            default = None

        if default is not NO_DEFAULT:
            self.default = default

        self.title = title
        self.description = description
        self.allow_null = allow_null

        # We need this global counter to determine what order fields have
        # been declared in when used with `Schema`.
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

    def get_default_value(self) -> typing.Any:
        default = getattr(self, "default", None)
        if callable(default):
            return default()
        return default


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Union(Field):

    errors = {"null": "May not be null.", "union": "Did not match any valid type."}

    def __init__(self, any_of: typing.List[Field], **kwargs: typing.Any):
        super().__init__(**kwargs)

        self.any_of = any_of
        if any([child.allow_null for child in any_of]):
            self.allow_null = True

class Field:

    errors: typing.Dict[str, str] = {}

    _creation_counter = 0

    def __init__(
        self,
        *,
        title: str = "",
        description: str = "",
        default: typing.Any = NO_DEFAULT,
        allow_null: bool = False,
    ):
        assert isinstance(title, str)
        assert isinstance(description, str)

        if allow_null and default is NO_DEFAULT:
            default = None

        if default is not NO_DEFAULT:
            self.default = default

        self.title = title
        self.description = description
        self.allow_null = allow_null

        # We need this global counter to determine what order fields have
        # been declared in when used with `Schema`.
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

    def __or__(self, other: "Field") -> "Union":
        if isinstance(self, Union):
            any_of = self.any_of
        else:
            any_of = [self]

        if isinstance(other, Union):
            any_of += other.any_of
        else:
            any_of += [other]

        return Union(any_of=any_of)


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Union(Field):

    errors = {"null": "May not be null.", "union": "Did not match any valid type."}

    def __init__(self, any_of: typing.List[Field], **kwargs: typing.Any):
        super().__init__(**kwargs)

        self.any_of = any_of
        if any([child.allow_null for child in any_of]):
            self.allow_null = True

class String(Field):

    errors = {
        "type": "Must be a string.",
        "null": "May not be null.",
        "blank": "Must not be blank.",
        "max_length": "Must have no more than {max_length} characters.",
        "min_length": "Must have at least {min_length} characters.",
        "pattern": "Must match the pattern /{pattern}/.",
        "format": "Must be a valid {format}.",
    }

    def __init__(
        self,
        *,
        allow_blank: bool = False,
        trim_whitespace: bool = True,
        max_length: int = None,
        min_length: int = None,
        pattern: typing.Union[str, typing.Pattern] = None,
        format: str = None,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        assert max_length is None or isinstance(max_length, int)
        assert min_length is None or isinstance(min_length, int)
        assert pattern is None or isinstance(pattern, (str, typing.Pattern))
        assert format is None or isinstance(format, str)

        if allow_blank and not self.has_default():
            self.default = ""

        self.allow_blank = allow_blank
        self.trim_whitespace = trim_whitespace
        self.max_length = max_length
        self.min_length = min_length
        self.format = format

        if pattern is None:
            self.pattern = None
            self.pattern_regex = None
        elif isinstance(pattern, str):
            self.pattern = pattern
            self.pattern_regex = re.compile(pattern)
        else:
            self.pattern = pattern.pattern
            self.pattern_regex = pattern


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class String(Field):

    errors = {
        "type": "Must be a string.",
        "null": "May not be null.",
        "blank": "Must not be blank.",
        "max_length": "Must have no more than {max_length} characters.",
        "min_length": "Must have at least {min_length} characters.",
        "pattern": "Must match the pattern /{pattern}/.",
        "format": "Must be a valid {format}.",
    }

    def __init__(
        self,
        *,
        allow_blank: bool = False,
        trim_whitespace: bool = True,
        max_length: int = None,
        min_length: int = None,
        pattern: typing.Union[str, typing.Pattern] = None,
        format: str = None,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        assert max_length is None or isinstance(max_length, int)
        assert min_length is None or isinstance(min_length, int)
        assert pattern is None or isinstance(pattern, (str, typing.Pattern))
        assert format is None or isinstance(format, str)

        if allow_blank and not self.has_default():
            self.default = ""

        self.allow_blank = allow_blank
        self.trim_whitespace = trim_whitespace
        self.max_length = max_length
        self.min_length = min_length
        self.format = format

        if pattern is None:
            self.pattern = None
            self.pattern_regex = None
        elif isinstance(pattern, str):
            self.pattern = pattern
            self.pattern_regex = re.compile(pattern)
        else:
            self.pattern = pattern.pattern
            self.pattern_regex = pattern

    def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
        if value is None and self.allow_null:
            return None
        elif value is None and self.allow_blank and not strict:
            # Leniently cast nulls to empty strings if allow_blank.
            return ""
        elif value is None:
            raise self.validation_error("null")
        elif self.format in FORMATS and FORMATS[self.format].is_native_type(value):
            return value
        elif not isinstance(value, str):
            raise self.validation_error("type")

        # The null character is always invalid.
        value = value.replace("\0", "")

        # Strip leading/trailing whitespace by default.
        if self.trim_whitespace:
            value = value.strip()

        if not self.allow_blank and not value:
            if self.allow_null and not strict:
                # Leniently cast empty strings (after trimming) to null if allow_null.
                return None
            raise self.validation_error("blank")

        if self.min_length is not None:
            if len(value) < self.min_length:
                raise self.validation_error("min_length")

        if self.max_length is not None:
            if len(value) > self.max_length:
                raise self.validation_error("max_length")

        if self.pattern_regex is not None:
            if not self.pattern_regex.search(value):
                raise self.validation_error("pattern")

        if self.format in FORMATS:
            return FORMATS[self.format].validate(value)

        return value


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class String(Field):

    errors = {
        "type": "Must be a string.",
        "null": "May not be null.",
        "blank": "Must not be blank.",
        "max_length": "Must have no more than {max_length} characters.",
        "min_length": "Must have at least {min_length} characters.",
        "pattern": "Must match the pattern /{pattern}/.",
        "format": "Must be a valid {format}.",
    }

    def __init__(
        self,
        *,
        allow_blank: bool = False,
        trim_whitespace: bool = True,
        max_length: int = None,
        min_length: int = None,
        pattern: typing.Union[str, typing.Pattern] = None,
        format: str = None,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        assert max_length is None or isinstance(max_length, int)
        assert min_length is None or isinstance(min_length, int)
        assert pattern is None or isinstance(pattern, (str, typing.Pattern))
        assert format is None or isinstance(format, str)

        if allow_blank and not self.has_default():
            self.default = ""

        self.allow_blank = allow_blank
        self.trim_whitespace = trim_whitespace
        self.max_length = max_length
        self.min_length = min_length
        self.format = format

        if pattern is None:
            self.pattern = None
            self.pattern_regex = None
        elif isinstance(pattern, str):
            self.pattern = pattern
            self.pattern_regex = re.compile(pattern)
        else:
            self.pattern = pattern.pattern
            self.pattern_regex = pattern

    def serialize(self, obj: typing.Any) -> typing.Any:
        if self.format in FORMATS:
            return FORMATS[self.format].serialize(obj)
        return obj


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Number(Field):

    numeric_type: typing.Optional[type] = None

    errors = {
        "type": "Must be a number.",
        "null": "May not be null.",
        "integer": "Must be an integer.",
        "finite": "Must be finite.",
        "minimum": "Must be greater than or equal to {minimum}.",
        "exclusive_minimum": "Must be greater than {exclusive_minimum}.",
        "maximum": "Must be less than or equal to {maximum}.",
        "exclusive_maximum": "Must be less than {exclusive_maximum}.",
        "multiple_of": "Must be a multiple of {multiple_of}.",
    }

    def __init__(
        self,
        *,
        minimum: typing.Union[int, float, decimal.Decimal] = None,
        maximum: typing.Union[int, float, decimal.Decimal] = None,
        exclusive_minimum: typing.Union[int, float, decimal.Decimal] = None,
        exclusive_maximum: typing.Union[int, float, decimal.Decimal] = None,
        precision: str = None,
        multiple_of: typing.Union[int, float, decimal.Decimal] = None,
        **kwargs: typing.Any,
    ):
        super().__init__(**kwargs)

        assert minimum is None or isinstance(minimum, (int, float, decimal.Decimal))
        assert maximum is None or isinstance(maximum, (int, float, decimal.Decimal))
        assert exclusive_minimum is None or isinstance(
            exclusive_minimum, (int, float, decimal.Decimal)
        )
        assert exclusive_maximum is None or isinstance(
            exclusive_maximum, (int, float, decimal.Decimal)
        )
        assert multiple_of is None or isinstance(
            multiple_of, (int, float, decimal.Decimal)
        )

        self.minimum = minimum
        self.maximum = maximum
        self.exclusive_minimum = exclusive_minimum
        self.exclusive_maximum = exclusive_maximum
        self.multiple_of = multiple_of
        self.precision = precision

    def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
        if value is None and self.allow_null:
            return None
        elif value == "" and self.allow_null and not strict:
            return None
        elif value is None:
            raise self.validation_error("null")
        elif isinstance(value, bool):
            raise self.validation_error("type")
        elif (
            self.numeric_type is int
            and isinstance(value, float)
            and not value.is_integer()
        ):
            raise self.validation_error("integer")
        elif not isinstance(value, (int, float)) and strict:
            raise self.validation_error("type")

        try:
            if isinstance(value, str):
                # Casting to a decimal first gives more lenient parsing.
                value = decimal.Decimal(value)
            if self.numeric_type is not None:
                value = self.numeric_type(value)
        except (TypeError, ValueError, decimal.InvalidOperation):
            raise self.validation_error("type")

        if not isfinite(value):
            # inf, -inf, nan, are all invalid.
            raise self.validation_error("finite")

        if self.precision is not None:
            numeric_type = self.numeric_type or type(value)
            quantize_val = decimal.Decimal(self.precision)
            decimal_val = decimal.Decimal(value)
            decimal_val = decimal_val.quantize(
                quantize_val, rounding=decimal.ROUND_HALF_UP
            )
            value = numeric_type(decimal_val)

        if self.minimum is not None and value < self.minimum:
            raise self.validation_error("minimum")

        if self.exclusive_minimum is not None and value <= self.exclusive_minimum:
            raise self.validation_error("exclusive_minimum")

        if self.maximum is not None and value > self.maximum:
            raise self.validation_error("maximum")

        if self.exclusive_maximum is not None and value >= self.exclusive_maximum:
            raise self.validation_error("exclusive_maximum")

        if self.multiple_of is not None:
            if isinstance(self.multiple_of, int):
                if value % self.multiple_of:
                    raise self.validation_error("multiple_of")
            else:
                if not (value * (1 / self.multiple_of)).is_integer():
                    raise self.validation_error("multiple_of")

        return value


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Boolean(Field):

    errors = {"type": "Must be a boolean.", "null": "May not be null."}

    coerce_values = {
        "true": True,
        "false": False,
        "on": True,
        "off": False,
        "1": True,
        "0": False,
        "": False,
        1: True,
        0: False,
    }

    coerce_null_values = {"", "null", "none"}

    def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
        if value is None and self.allow_null:
            return None

        elif value is None:
            raise self.validation_error("null")

        elif not isinstance(value, bool):
            if strict:
                raise self.validation_error("type")

            if isinstance(value, str):
                value = value.lower()

            if self.allow_null and value in self.coerce_null_values:
                return None

            try:
                value = self.coerce_values[value]
            except (KeyError, TypeError):
                raise self.validation_error("type")

        return value


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Union(Field):

    errors = {"null": "May not be null.", "union": "Did not match any valid type."}

    def __init__(self, any_of: typing.List[Field], **kwargs: typing.Any):
        super().__init__(**kwargs)

        self.any_of = any_of
        if any([child.allow_null for child in any_of]):
            self.allow_null = True

class Choice(Field):

    errors = {
        "null": "May not be null.",
        "required": "This field is required.",
        "choice": "Not a valid choice.",
    }

    def __init__(
        self,
        *,
        choices: typing.Sequence[typing.Union[str, typing.Tuple[str, str]]] = None,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)
        self.choices = [
            (choice if isinstance(choice, (tuple, list)) else (choice, choice))
            for choice in choices or []
        ]
        assert all(len(choice) == 2 for choice in self.choices)


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Choice(Field):

    errors = {
        "null": "May not be null.",
        "required": "This field is required.",
        "choice": "Not a valid choice.",
    }

    def __init__(
        self,
        *,
        choices: typing.Sequence[typing.Union[str, typing.Tuple[str, str]]] = None,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)
        self.choices = [
            (choice if isinstance(choice, (tuple, list)) else (choice, choice))
            for choice in choices or []
        ]
        assert all(len(choice) == 2 for choice in self.choices)

    def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
        if value is None and self.allow_null:
            return None
        elif value is None:
            raise self.validation_error("null")
        elif value not in Uniqueness([key for key, value in self.choices]):
            if value == "":
                if self.allow_null and not strict:
                    return None
                raise self.validation_error("required")
            raise self.validation_error("choice")
        return value


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Field:

    errors: typing.Dict[str, str] = {}

    _creation_counter = 0

    def __init__(
        self,
        *,
        title: str = "",
        description: str = "",
        default: typing.Any = NO_DEFAULT,
        allow_null: bool = False,
    ):
        assert isinstance(title, str)
        assert isinstance(description, str)

        if allow_null and default is NO_DEFAULT:
            default = None

        if default is not NO_DEFAULT:
            self.default = default

        self.title = title
        self.description = description
        self.allow_null = allow_null

        # We need this global counter to determine what order fields have
        # been declared in when used with `Schema`.
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

class Object(Field):

    errors = {
        "type": "Must be an object.",
        "null": "May not be null.",
        "invalid_key": "All object keys must be strings.",
        "required": "This field is required.",
        "invalid_property": "Invalid property name.",
        "empty": "Must not be empty.",
        "max_properties": "Must have no more than {max_properties} properties.",
        "min_properties": "Must have at least {min_properties} properties.",
    }

    def __init__(
        self,
        *,
        properties: typing.Dict[str, Field] = None,
        pattern_properties: typing.Dict[str, Field] = None,
        additional_properties: typing.Union[bool, None, Field] = True,
        property_names: Field = None,
        min_properties: int = None,
        max_properties: int = None,
        required: typing.Sequence[str] = None,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        if isinstance(properties, Field):
            additional_properties = properties
            properties = None

        properties = {} if (properties is None) else dict(properties)
        pattern_properties = (
            {} if (pattern_properties is None) else dict(pattern_properties)
        )
        required = list(required) if isinstance(required, (list, tuple)) else required
        required = [] if (required is None) else required

        assert all(isinstance(k, str) for k in properties.keys())
        assert all(isinstance(v, Field) for v in properties.values())
        assert all(isinstance(k, str) for k in pattern_properties.keys())
        assert all(isinstance(v, Field) for v in pattern_properties.values())
        assert additional_properties in (None, True, False) or isinstance(
            additional_properties, Field
        )
        assert min_properties is None or isinstance(min_properties, int)
        assert max_properties is None or isinstance(max_properties, int)
        assert all(isinstance(i, str) for i in required)

        self.properties = properties
        self.pattern_properties = pattern_properties
        self.additional_properties = additional_properties
        self.property_names = property_names
        self.min_properties = min_properties
        self.max_properties = max_properties
        self.required = required

    def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
        if value is None and self.allow_null:
            return None
        elif value is None:
            raise self.validation_error("null")
        elif not isinstance(value, (dict, typing.Mapping)):
            raise self.validation_error("type")

        validated = {}
        error_messages = []

        # Ensure all property keys are strings.
        for key in value.keys():
            if not isinstance(key, str):
                text = self.get_error_text("invalid_key")
                message = Message(text=text, code="invalid_key", index=[key])
                error_messages.append(message)
            elif self.property_names is not None:
                _, error = self.property_names.validate_or_error(key)
                if error is not None:
                    text = self.get_error_text("invalid_property")
                    message = Message(text=text, code="invalid_property", index=[key])
                    error_messages.append(message)

        # Min/Max properties
        if self.min_properties is not None:
            if len(value) < self.min_properties:
                if self.min_properties == 1:
                    raise self.validation_error("empty")
                else:
                    raise self.validation_error("min_properties")
        if self.max_properties is not None:
            if len(value) > self.max_properties:
                raise self.validation_error("max_properties")

        # Required properties
        for key in self.required:
            if key not in value:
                text = self.get_error_text("required")
                message = Message(text=text, code="required", index=[key])
                error_messages.append(message)

        # Properties
        for key, child_schema in self.properties.items():
            if key not in value:
                if child_schema.has_default():
                    validated[key] = child_schema.get_default_value()
                continue
            item = value[key]
            child_value, error = child_schema.validate_or_error(item, strict=strict)
            if not error:
                validated[key] = child_value
            else:
                error_messages += error.messages(add_prefix=key)

        # Pattern properties
        if self.pattern_properties:
            for key in list(value.keys()):
                for pattern, child_schema in self.pattern_properties.items():
                    if isinstance(key, str) and re.search(pattern, key):
                        item = value[key]
                        child_value, error = child_schema.validate_or_error(
                            item, strict=strict
                        )
                        if not error:
                            validated[key] = child_value
                        else:
                            error_messages += error.messages(add_prefix=key)

        # Additional properties
        validated_keys = set(validated.keys())
        error_keys = set(
            [message.index[0] for message in error_messages if message.index]
        )

        remaining = [
            key for key in value.keys() if key not in validated_keys | error_keys
        ]

        if self.additional_properties is True:
            for key in remaining:
                validated[key] = value[key]
        elif self.additional_properties is False:
            for key in remaining:
                text = self.get_error_text("invalid_property")
                message = Message(text=text, code="invalid_property", key=key)
                error_messages.append(message)
        elif self.additional_properties is not None:
            assert isinstance(self.additional_properties, Field)
            child_schema = self.additional_properties
            for key in remaining:
                item = value[key]
                child_value, error = child_schema.validate_or_error(item, strict=strict)
                if not error:
                    validated[key] = child_value
                else:
                    error_messages += error.messages(add_prefix=key)

        if error_messages:
            raise ValidationError(messages=error_messages)

        return validated


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Field:

    errors: typing.Dict[str, str] = {}

    _creation_counter = 0

    def __init__(
        self,
        *,
        title: str = "",
        description: str = "",
        default: typing.Any = NO_DEFAULT,
        allow_null: bool = False,
    ):
        assert isinstance(title, str)
        assert isinstance(description, str)

        if allow_null and default is NO_DEFAULT:
            default = None

        if default is not NO_DEFAULT:
            self.default = default

        self.title = title
        self.description = description
        self.allow_null = allow_null

        # We need this global counter to determine what order fields have
        # been declared in when used with `Schema`.
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

class Union(Field):

    errors = {"null": "May not be null.", "union": "Did not match any valid type."}

    def __init__(self, any_of: typing.List[Field], **kwargs: typing.Any):
        super().__init__(**kwargs)

        self.any_of = any_of
        if any([child.allow_null for child in any_of]):
            self.allow_null = True

class Array(Field):

    errors = {
        "type": "Must be an array.",
        "null": "May not be null.",
        "empty": "Must not be empty.",
        "exact_items": "Must have {min_items} items.",
        "min_items": "Must have at least {min_items} items.",
        "max_items": "Must have no more than {max_items} items.",
        "additional_items": "May not contain additional items.",
        "unique_items": "Items must be unique.",
    }

    def __init__(
        self,
        items: typing.Union[Field, typing.Sequence[Field]] = None,
        additional_items: typing.Union[Field, bool] = False,
        min_items: int = None,
        max_items: int = None,
        exact_items: int = None,
        unique_items: bool = False,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        items = list(items) if isinstance(items, (list, tuple)) else items

        assert (
            items is None
            or isinstance(items, Field)
            or (isinstance(items, list) and all(isinstance(i, Field) for i in items))
        )
        assert isinstance(additional_items, bool) or isinstance(additional_items, Field)
        assert min_items is None or isinstance(min_items, int)
        assert max_items is None or isinstance(max_items, int)
        assert isinstance(unique_items, bool)

        if isinstance(items, list):
            if min_items is None:
                min_items = len(items)
            if max_items is None and (additional_items is False):
                max_items = len(items)

        if exact_items is not None:
            min_items = exact_items
            max_items = exact_items

        self.items = items
        self.additional_items = additional_items
        self.min_items = min_items
        self.max_items = max_items
        self.unique_items = unique_items


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Field:

    errors: typing.Dict[str, str] = {}

    _creation_counter = 0

    def __init__(
        self,
        *,
        title: str = "",
        description: str = "",
        default: typing.Any = NO_DEFAULT,
        allow_null: bool = False,
    ):
        assert isinstance(title, str)
        assert isinstance(description, str)

        if allow_null and default is NO_DEFAULT:
            default = None

        if default is not NO_DEFAULT:
            self.default = default

        self.title = title
        self.description = description
        self.allow_null = allow_null

        # We need this global counter to determine what order fields have
        # been declared in when used with `Schema`.
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

class Array(Field):

    errors = {
        "type": "Must be an array.",
        "null": "May not be null.",
        "empty": "Must not be empty.",
        "exact_items": "Must have {min_items} items.",
        "min_items": "Must have at least {min_items} items.",
        "max_items": "Must have no more than {max_items} items.",
        "additional_items": "May not contain additional items.",
        "unique_items": "Items must be unique.",
    }

    def __init__(
        self,
        items: typing.Union[Field, typing.Sequence[Field]] = None,
        additional_items: typing.Union[Field, bool] = False,
        min_items: int = None,
        max_items: int = None,
        exact_items: int = None,
        unique_items: bool = False,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        items = list(items) if isinstance(items, (list, tuple)) else items

        assert (
            items is None
            or isinstance(items, Field)
            or (isinstance(items, list) and all(isinstance(i, Field) for i in items))
        )
        assert isinstance(additional_items, bool) or isinstance(additional_items, Field)
        assert min_items is None or isinstance(min_items, int)
        assert max_items is None or isinstance(max_items, int)
        assert isinstance(unique_items, bool)

        if isinstance(items, list):
            if min_items is None:
                min_items = len(items)
            if max_items is None and (additional_items is False):
                max_items = len(items)

        if exact_items is not None:
            min_items = exact_items
            max_items = exact_items

        self.items = items
        self.additional_items = additional_items
        self.min_items = min_items
        self.max_items = max_items
        self.unique_items = unique_items

    def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any:
        if value is None and self.allow_null:
            return None
        elif value is None:
            raise self.validation_error("null")
        elif not isinstance(value, list):
            raise self.validation_error("type")

        if (
            self.min_items is not None
            and self.min_items == self.max_items
            and len(value) != self.min_items
        ):
            raise self.validation_error("exact_items")
        if self.min_items is not None and len(value) < self.min_items:
            if self.min_items == 1:
                raise self.validation_error("empty")
            raise self.validation_error("min_items")
        elif self.max_items is not None and len(value) > self.max_items:
            raise self.validation_error("max_items")

        # Ensure all items are of the right type.
        validated = []
        error_messages: typing.List[Message] = []
        if self.unique_items:
            seen_items = Uniqueness()

        for pos, item in enumerate(value):
            validator = None
            if isinstance(self.items, list):
                if pos < len(self.items):
                    validator = self.items[pos]
                elif isinstance(self.additional_items, Field):
                    validator = self.additional_items
            elif self.items is not None:
                validator = self.items

            if validator is None:
                validated.append(item)
            else:
                item, error = validator.validate_or_error(item, strict=strict)
                if error:
                    error_messages += error.messages(add_prefix=pos)
                else:
                    validated.append(item)

            if self.unique_items:
                if item in seen_items:
                    text = self.get_error_text("unique_items")
                    message = Message(text=text, code="unique_items", key=pos)
                    error_messages.append(message)
                else:
                    seen_items.add(item)

        if error_messages:
            raise ValidationError(messages=error_messages)

        return validated


import decimal
import re
import typing
from math import isfinite
from typesystem import formats
from typesystem.base import Message, ValidationError, ValidationResult
from typesystem.unique import Uniqueness

NO_DEFAULT = object()
FORMATS = {
    "date": formats.DateFormat(),
    "time": formats.TimeFormat(),
    "datetime": formats.DateTimeFormat(),
    "uuid": formats.UUIDFormat(),
}

class Array(Field):

    errors = {
        "type": "Must be an array.",
        "null": "May not be null.",
        "empty": "Must not be empty.",
        "exact_items": "Must have {min_items} items.",
        "min_items": "Must have at least {min_items} items.",
        "max_items": "Must have no more than {max_items} items.",
        "additional_items": "May not contain additional items.",
        "unique_items": "Items must be unique.",
    }

    def __init__(
        self,
        items: typing.Union[Field, typing.Sequence[Field]] = None,
        additional_items: typing.Union[Field, bool] = False,
        min_items: int = None,
        max_items: int = None,
        exact_items: int = None,
        unique_items: bool = False,
        **kwargs: typing.Any,
    ) -> None:
        super().__init__(**kwargs)

        items = list(items) if isinstance(items, (list, tuple)) else items

        assert (
            items is None
            or isinstance(items, Field)
            or (isinstance(items, list) and all(isinstance(i, Field) for i in items))
        )
        assert isinstance(additional_items, bool) or isinstance(additional_items, Field)
        assert min_items is None or isinstance(min_items, int)
        assert max_items is None or isinstance(max_items, int)
        assert isinstance(unique_items, bool)

        if isinstance(items, list):
            if min_items is None:
                min_items = len(items)
            if max_items is None and (additional_items is False):
                max_items = len(items)

        if exact_items is not None:
            min_items = exact_items
            max_items = exact_items

        self.items = items
        self.additional_items = additional_items
        self.min_items = min_items
        self.max_items = max_items
        self.unique_items = unique_items

    def serialize(self, obj: typing.Any) -> typing.Any:
        if obj is None:
            return None

        if isinstance(self.items, list):
            return [
                serializer.serialize(value)
                for serializer, value in zip(self.items, obj)
            ]

        if self.items is None:
            return obj

        return [self.items.serialize(value) for value in obj]