Source code for flask_admin.tools

import sys
import traceback
import typing as t
from functools import reduce
from types import ModuleType

# Python 3 compatibility
from ._compat import as_unicode

CHAR_ESCAPE = "."
CHAR_SEPARATOR = ","


[docs] def import_module(name: str, required: bool = True) -> ModuleType | None: """ Import module by name :param name: Module name :param required: If set to `True` and module was not found - will throw exception. If set to `False` and module was not found - will return None. Default is `True`. """ try: __import__(name, globals(), locals(), []) except ImportError: if not required and module_not_found(): return None raise return sys.modules[name]
[docs] def import_attribute(name: str) -> t.Any: """ Import attribute using string reference. :param name: String reference. Raises ImportError or AttributeError if module or attribute do not exist. Example:: import_attribute('a.b.c.foo') """ path, attr = name.rsplit(".", 1) module = __import__(path, globals(), locals(), [attr]) return getattr(module, attr)
[docs] def module_not_found(additional_depth: int = 0) -> bool: """ Checks if ImportError was raised because module does not exist or something inside it raised ImportError :param additional_depth: supply int of depth of your call if you're not doing import on the same level of code - f.e., if you call function, which is doing import, you should pass 1 for single additional level of depth """ tb = sys.exc_info()[2] if len(traceback.extract_tb(tb)) > (1 + additional_depth): return False return True
[docs] def rec_getattr(obj: t.Any, attr: str, default: t.Any = None) -> t.Any: """ Recursive getattr. :param attr: Dot delimited attribute name :param default: Default value Example:: rec_getattr(obj, 'a.b.c') """ try: return reduce(getattr, attr.split("."), obj) except AttributeError: return default
def get_dict_attr(obj: t.Any, attr: str, default: t.Any = None) -> t.Any | None: """ Get attribute of the object without triggering its __getattr__. :param obj: Object :param attr: Attribute name :param default: Default value if attribute was not found """ for o in [obj] + obj.__class__.mro(): if attr in o.__dict__: return o.__dict__[attr] return default def escape(value: str | bytes) -> str: return ( as_unicode(value) .replace(CHAR_ESCAPE, CHAR_ESCAPE + CHAR_ESCAPE) .replace(CHAR_SEPARATOR, CHAR_ESCAPE + CHAR_SEPARATOR) ) def iterencode(iter: t.Iterable[str | bytes | int]) -> str: """ Encode enumerable as compact string representation. :param iter: Enumerable """ return ",".join( as_unicode(v) .replace(CHAR_ESCAPE, CHAR_ESCAPE + CHAR_ESCAPE) .replace(CHAR_SEPARATOR, CHAR_ESCAPE + CHAR_SEPARATOR) for v in iter ) def iterdecode(value: t.Iterable[str]) -> tuple[str, ...]: """ Decode enumerable from string presentation as a tuple """ if not value: return tuple() result = [] accumulator = "" escaped = False for c in value: if not escaped: if c == CHAR_ESCAPE: escaped = True continue elif c == CHAR_SEPARATOR: result.append(accumulator) accumulator = "" continue else: escaped = False accumulator += c result.append(accumulator) return tuple(result)