|
1 | | -from collections.abc import Sequence |
2 | | -from typing import Any |
3 | | -from typing import Callable |
4 | | -from typing import TYPE_CHECKING |
5 | | -from typing import Union |
| 1 | +import typing as t |
| 2 | +from enum import Enum |
| 3 | +from os import PathLike |
| 4 | +from types import TracebackType |
6 | 5 |
|
7 | | -if TYPE_CHECKING: |
| 6 | +import wtforms.widgets |
| 7 | +from flask import Response |
| 8 | +from jinja2.runtime import Context |
| 9 | +from markupsafe import Markup |
| 10 | +from typing_extensions import NotRequired |
| 11 | +from werkzeug.wrappers import Response as Wkzg_Response |
| 12 | +from wtforms import Field |
| 13 | +from wtforms.form import BaseForm |
| 14 | +from wtforms.utils import UnsetValue |
| 15 | +from wtforms.widgets import Input |
| 16 | + |
| 17 | +if t.TYPE_CHECKING: |
| 18 | + from flask_admin.base import BaseView as T_VIEW # noqa |
| 19 | + from flask_admin.contrib.sqla.validators import InputRequired as T_INPUT_REQUIRED |
| 20 | + from flask_admin.contrib.sqla.validators import ( |
| 21 | + TimeZoneValidator as T_TIMEZONE_VALIDATOR, |
| 22 | + ) |
| 23 | + from flask_admin.contrib.sqla.validators import Unique as T_UNIQUE |
| 24 | + from flask_admin.form import FormOpts as T_FORM_OPTS # noqa |
| 25 | + from flask_admin.model import BaseModelView as T_MODEL_VIEW |
| 26 | + from flask_admin.model.ajax import AjaxModelLoader as T_AJAX_MODEL_LOADER # noqa |
| 27 | + from flask_admin.model.fields import AjaxSelectField as T_AJAX_SELECT_FIELD # noqa |
| 28 | + from flask_admin.model.form import ( # noqa |
| 29 | + InlineBaseFormAdmin as T_INLINE_BASE_FORM_ADMIN, |
| 30 | + ) |
| 31 | + from flask_admin.model.form import InlineFormAdmin as T_INLINE_FORM_ADMIN |
| 32 | + from flask_admin.model.widgets import ( |
| 33 | + InlineFieldListWidget as T_INLINE_FIELD_LIST_WIDGET, |
| 34 | + ) |
| 35 | + from flask_admin.model.widgets import InlineFormWidget as T_INLINE_FORM_WIDGET |
| 36 | + from flask_admin.model.widgets import ( |
| 37 | + AjaxSelect2Widget as T_INLINE_AJAX_SELECT2_WIDGET, |
| 38 | + ) |
| 39 | + from flask_admin.model.widgets import XEditableWidget as T_INLINE_X_EDITABLE_WIDGET |
| 40 | + |
| 41 | + # optional dependencies |
| 42 | + from arrow import Arrow as T_ARROW # noqa |
| 43 | + from flask_babel import LazyString as T_LAZY_STRING # noqa |
| 44 | + |
| 45 | + from flask_sqlalchemy import Model as T_SQLALCHEMY_MODEL |
| 46 | + from flask_admin.contrib.peewee.form import BaseModel as T_PEEWEE_MODEL |
| 47 | + from peewee import Field as T_PEEWEE_FIELD # noqa |
| 48 | + from pymongo import MongoClient as T_MONGO_CLIENT |
8 | 49 | import sqlalchemy # noqa |
| 50 | + from sqlalchemy import Column as T_SQLALCHEMY_COLUMN |
| 51 | + from sqlalchemy import Table as T_TABLE # noqa |
| 52 | + from sqlalchemy.orm import scoped_session as T_SQLALCHEMY_SESSION # noqa |
| 53 | + from sqlalchemy.orm.query import Query |
| 54 | + from sqlalchemy.sql.selectable import Select |
| 55 | + |
| 56 | + T_SQLALCHEMY_QUERY = t.Union[Query, Select] |
| 57 | + from redis import Redis as T_REDIS # noqa |
| 58 | + from flask_admin.contrib.peewee.ajax import ( |
| 59 | + QueryAjaxModelLoader as T_PEEWEE_QUERY_AJAX_MODEL_LOADER, |
| 60 | + ) # noqa |
| 61 | + from flask_admin.contrib.sqla.ajax import ( |
| 62 | + QueryAjaxModelLoader as T_SQLA_QUERY_AJAX_MODEL_LOADER, |
| 63 | + ) # noqa |
| 64 | +else: |
| 65 | + T_VIEW = "flask_admin.base.BaseView" |
| 66 | + T_INPUT_REQUIRED = "InputRequired" |
| 67 | + T_TIMEZONE_VALIDATOR = "TimeZoneValidator" |
| 68 | + T_UNIQUE = "Unique" |
| 69 | + T_FORM_OPTS = "flask_admin.form.FormOpts" |
| 70 | + T_MODEL_VIEW = "flask_admin.model.BaseModelView" |
| 71 | + T_AJAX_MODEL_LOADER = "flask_admin.model.ajax.AjaxModelLoader" |
| 72 | + T_AJAX_SELECT_FIELD = "flask_admin.model.fields.AjaxSelectField" |
| 73 | + T_INLINE_BASE_FORM_ADMIN = "flask_admin.model.form.InlineBaseFormAdmin" |
| 74 | + T_INLINE_FORM_ADMIN = "flask_admin.model.form.InlineFormAdmin" |
| 75 | + T_INLINE_FIELD_LIST_WIDGET = "flask_admin.model.widgets.InlineFieldListWidget" |
| 76 | + T_INLINE_FORM_WIDGET = "flask_admin.model.widgets.InlineFormWidget" |
| 77 | + T_INLINE_AJAX_SELECT2_WIDGET = "flask_admin.model.widgets.AjaxSelect2Widget" |
| 78 | + T_INLINE_X_EDITABLE_WIDGET = "flask_admin.model.widgets.XEditableWidget" |
| 79 | + |
| 80 | + # optional dependencies |
| 81 | + T_ARROW = "arrow.Arrow" |
| 82 | + T_LAZY_STRING = "flask_babel.LazyString" |
| 83 | + T_SQLALCHEMY_COLUMN = "sqlalchemy.Column" |
| 84 | + T_SQLALCHEMY_MODEL = "flask_sqlalchemy.Model" |
| 85 | + T_PEEWEE_FIELD = "peewee.Field" |
| 86 | + T_PEEWEE_MODEL = "peewee.BaseModel" |
| 87 | + T_MONGO_CLIENT = "pymongo.MongoClient" |
| 88 | + T_TABLE = "sqlalchemy.Table" |
| 89 | + T_SQLALCHEMY_QUERY = t.Union[ |
| 90 | + "sqlalchemy.sql.selectable.Select", "sqlalchemy.orm.query.Query" |
| 91 | + ] |
| 92 | + T_SQLALCHEMY_SESSION = "sqlalchemy.orm.scoped_session" |
| 93 | + T_REDIS = "redis.Redis" |
| 94 | + T_PEEWEE_QUERY_AJAX_MODEL_LOADER = ( |
| 95 | + "flask_admin.contrib.peewee.ajax.QueryAjaxModelLoader" |
| 96 | + ) |
| 97 | + T_SQLA_QUERY_AJAX_MODEL_LOADER = ( |
| 98 | + "flask_admin.contrib.sqla.ajax.QueryAjaxModelLoader" |
| 99 | + ) |
| 100 | + |
| 101 | +T_COLUMN = t.Union[str, T_SQLALCHEMY_COLUMN] |
| 102 | +T_FILTER = tuple[int, T_COLUMN, str] |
| 103 | +T_COLUMN_LIST = t.Sequence[T_COLUMN] |
| 104 | +# Compatibility for 3-tuples and 4-tuples in iter_choices |
| 105 | +# https://wtforms.readthedocs.io/en/3.2.x/changes/#version-3-2-0 |
| 106 | +T_ITER_CHOICES = t.Union[ |
| 107 | + tuple[t.Any, str, bool, dict[str, t.Any]], tuple[str, str, bool] |
| 108 | +] |
| 109 | +T_FORMATTER = t.Callable[[T_MODEL_VIEW, t.Optional[Context], t.Any, str], str] |
| 110 | +T_COLUMN_FORMATTERS = dict[str, T_FORMATTER] |
| 111 | +T_TYPE_FORMATTER = t.Callable[[T_MODEL_VIEW, t.Any, str], t.Union[str, Markup]] |
| 112 | +T_COLUMN_TYPE_FORMATTERS = dict[type, T_TYPE_FORMATTER] |
| 113 | +T_TRANSLATABLE = t.Union[str, T_LAZY_STRING] |
| 114 | +T_OPTION = tuple[str, T_TRANSLATABLE] |
| 115 | +T_OPTION_LIST = t.Sequence[T_OPTION] |
| 116 | +T_OPTIONS = t.Union[None, T_OPTION_LIST, t.Callable[[], T_OPTION_LIST]] |
| 117 | +T_ORM_MODEL = t.Union[T_SQLALCHEMY_MODEL, T_PEEWEE_MODEL, T_MONGO_CLIENT] |
| 118 | +T_QUERY_AJAX_MODEL_LOADER = t.Union[ |
| 119 | + T_PEEWEE_QUERY_AJAX_MODEL_LOADER, T_SQLA_QUERY_AJAX_MODEL_LOADER |
| 120 | +] |
| 121 | +T_RESPONSE = t.Union[Response, Wkzg_Response] |
| 122 | +T_SQLALCHEMY_INLINE_MODELS = t.Union[ |
| 123 | + t.Sequence[t.Union[T_INLINE_FORM_ADMIN, T_SQLALCHEMY_MODEL]], |
| 124 | + tuple[T_SQLALCHEMY_MODEL, dict[str, t.Any]], |
| 125 | +] |
| 126 | +T_VALIDATOR = t.Union[ |
| 127 | + t.Callable[[t.Any, t.Any], t.Any], |
| 128 | + T_UNIQUE, |
| 129 | + T_INPUT_REQUIRED, |
| 130 | + wtforms.validators.Optional, |
| 131 | + wtforms.validators.Length, |
| 132 | + wtforms.validators.AnyOf, |
| 133 | + wtforms.validators.Email, |
| 134 | + wtforms.validators.URL, |
| 135 | + wtforms.validators.IPAddress, |
| 136 | + T_TIMEZONE_VALIDATOR, |
| 137 | + wtforms.validators.NumberRange, |
| 138 | + wtforms.validators.MacAddress, |
| 139 | +] |
| 140 | +T_PATH_LIKE = t.Union[str, bytes, PathLike[str], PathLike[bytes]] |
| 141 | + |
| 142 | + |
| 143 | +class WidgetProtocol(t.Protocol): |
| 144 | + def __call__(self, field: Field, **kwargs: t.Any) -> t.Union[str, Markup]: ... |
| 145 | + |
| 146 | + |
| 147 | +T_WIDGET = t.Union[ |
| 148 | + Input, |
| 149 | + T_INLINE_FIELD_LIST_WIDGET, |
| 150 | + T_INLINE_FORM_WIDGET, |
| 151 | + T_INLINE_AJAX_SELECT2_WIDGET, |
| 152 | + T_INLINE_X_EDITABLE_WIDGET, |
| 153 | + WidgetProtocol, |
| 154 | +] |
| 155 | + |
| 156 | +T_WIDGET_TYPE = t.Optional[ |
| 157 | + t.Union[ |
| 158 | + t.Literal[ |
| 159 | + "daterangepicker", |
| 160 | + "datetimepicker", |
| 161 | + "datetimerangepicker", |
| 162 | + "datepicker", |
| 163 | + "select2-tags", |
| 164 | + "timepicker", |
| 165 | + "timerangepicker", |
| 166 | + "uuid", |
| 167 | + ], |
| 168 | + str, |
| 169 | + ] |
| 170 | +] |
| 171 | + |
| 172 | + |
| 173 | +class T_FIELD_ARGS_DESCRIPTION(t.TypedDict, total=False): |
| 174 | + description: NotRequired[str] |
| 175 | + |
| 176 | + |
| 177 | +class T_FIELD_ARGS_FILTERS(t.TypedDict): |
| 178 | + filters: NotRequired[list[t.Callable[[t.Any], t.Any]]] |
| 179 | + allow_blank: NotRequired[bool] |
| 180 | + choices: NotRequired[t.Union[list[tuple[int, str]], list[Enum]]] |
| 181 | + validators: NotRequired[list[T_VALIDATOR]] |
| 182 | + coerce: NotRequired[t.Callable[[t.Any], t.Any]] |
| 183 | + |
| 184 | + |
| 185 | +class T_FIELD_ARGS_LABEL(t.TypedDict): |
| 186 | + label: NotRequired[str] |
| 187 | + |
| 188 | + |
| 189 | +class T_FIELD_ARGS_PLACES(t.TypedDict): |
| 190 | + places: t.Optional[UnsetValue] |
| 191 | + |
| 192 | + |
| 193 | +class T_FIELD_ARGS_VALIDATORS(t.TypedDict, total=False): |
| 194 | + label: NotRequired[str] |
| 195 | + description: NotRequired[str] |
| 196 | + filters: NotRequired[list[t.Callable[[t.Any], t.Any]]] |
| 197 | + default: NotRequired[t.Any] |
| 198 | + widget: NotRequired[Input] |
| 199 | + validators: NotRequired[list[T_VALIDATOR]] |
| 200 | + render_kw: NotRequired[dict[str, t.Any]] |
| 201 | + name: NotRequired[str] |
| 202 | + _form: NotRequired[BaseForm] |
| 203 | + _prefix: NotRequired[str] |
| 204 | + |
| 205 | + |
| 206 | +class T_FIELD_ARGS_VALIDATORS_ALLOW_BLANK(T_FIELD_ARGS_VALIDATORS): |
| 207 | + allow_blank: NotRequired[bool] |
| 208 | + |
| 209 | + |
| 210 | +# Flask types |
| 211 | +_ExcInfo = tuple[ |
| 212 | + t.Optional[type[BaseException]], |
| 213 | + t.Optional[BaseException], |
| 214 | + t.Optional[TracebackType], |
| 215 | +] |
| 216 | +_StartResponse = t.Callable[ |
| 217 | + [str, list[tuple[str, str]], t.Optional[_ExcInfo]], t.Callable[[bytes], t.Any] |
| 218 | +] |
| 219 | +_WSGICallable = t.Callable[[dict[str, t.Any], _StartResponse], t.Iterable[bytes]] |
| 220 | +_Status = t.Union[str, int] |
| 221 | +_Headers = t.Union[dict[t.Any, t.Any], list[tuple[t.Any, t.Any]]] |
| 222 | +_Body = t.Union[str, t.ByteString, dict[str, t.Any], Response, _WSGICallable] |
| 223 | +_ViewFuncReturnType = t.Union[ |
| 224 | + _Body, |
| 225 | + tuple[_Body, _Status, _Headers], |
| 226 | + tuple[_Body, _Status], |
| 227 | + tuple[_Body, _Headers], |
| 228 | +] |
9 | 229 |
|
10 | | -T_COLUMN_LIST = Sequence[Union[str, "sqlalchemy.Column"]] |
11 | | -T_FORMATTER = Callable[[Any, Any, Any], Any] |
12 | | -T_FORMATTERS = dict[type, T_FORMATTER] |
| 230 | +_ViewFunc = t.Union[t.Callable[..., t.NoReturn], t.Callable[..., _ViewFuncReturnType]] |
0 commit comments