utils.global_messages 源代码

'''
global_messages.py

- 成功与错误提示信息的常量
- 读取、增添和传递信息
- 通过URL便捷传递信息
- 更容易读取请求和目录

@Author pht
@Date 2022-02-17
'''
# 类型信息提示
from typing import Union, TypedDict, Callable, Sequence, Any

__all__ = [
    # 常量
    'WRONG', 'SUCCEED',
    'CODE_FIELD', 'MSG_FIELD', 'ALERT_FIELD',
    # 类型信息支持
    'MESSAGECONTEXT',
    # 生成全局消息
    'wrong', 'succeed', 'alert',
    # 读取全局消息
    'get_warning', 'get_alert',
    # 转移全局消息
    'transfer_message_context',
    # 生成URL
    'append_query', 'message_url',
    # 读取其它目录类内容
    'read_key', 'read_content',
    'read_GET', 'read_POST',
]

# 常量
WRONG, SUCCEED = 1, 2
CODE_FIELD = 'warn_code'
MSG_FIELD = 'warn_message'
ALERT_FIELD = 'alert_message'

# 类型信息
MESSAGECONTEXT = TypedDict(
    'dict',
    warn_code=int, warn_message=str,
    alert_message=str,
)


# 生成全局消息
[文档] def wrong(message: str, context: dict = None) -> MESSAGECONTEXT: ''' 在错误的情况下返回的字典, message为错误信息 如果提供了context,则向其中添加信息 ''' if context is None: context = dict() context[CODE_FIELD] = WRONG context[MSG_FIELD] = message return context
[文档] def succeed(message: str, context: dict = None) -> MESSAGECONTEXT: ''' 在成功的情况下返回的字典, message为提示信息 如果提供了context,则向其中添加信息 ''' if context is None: context = dict() context[CODE_FIELD] = SUCCEED context[MSG_FIELD] = message return context
[文档] def alert(message: str, context: dict = None) -> MESSAGECONTEXT: if context is None: context = dict() context[ALERT_FIELD] = message return context
# 读取全局消息
[文档] def get_warning(source: dict, normalize=False): '''尝试以字典格式读取,失败时返回全None的元组,不抛出异常''' try: warn_code, warn_message = source[CODE_FIELD], source[MSG_FIELD] if normalize: warn_code, warn_message = int(warn_code), str(warn_message) assert warn_code in [WRONG, SUCCEED] except: warn_code, warn_message = None, None return warn_code, warn_message
[文档] def get_alert(source: dict, normalize=False): '''尝试以字典格式读取,失败时返回全None的元组,不抛出异常''' try: alert_message = source[ALERT_FIELD] if normalize: alert_message = str(alert_message) except: alert_message = None return alert_message
def get_all_message(source: dict, with_alert=False, normalize=False): '''尝试以字典格式读取,失败时返回全None的元组,不抛出异常''' warn_code, warn_message = get_warning(source, normalize) alert_message = get_alert(source, normalize) if with_alert else None return warn_code, warn_message, alert_message def get_request_message(request, with_alert=False): ''' 返回包含所有请求参数的元组,不抛出异常 按顺序分别返回(warn_code, warn_message, [alert_message, ]) 解析失败的部分返回None ''' result = list(get_warning(request.GET, normalize=True)) if with_alert: result.append(get_alert(request.GET, normalize=True)) return tuple(result) # 转移全局消息 def _move(context, warn_code, warn_message, alert_message=None): count = 0 if warn_code is not None: context[CODE_FIELD], context[MSG_FIELD] = warn_code, warn_message count += 1 if alert_message is not None: context[ALERT_FIELD] = alert_message count += 1 return count
[文档] def transfer_message_context(source: dict, context=None, with_alert=False, normalize=True) -> MESSAGECONTEXT: ''' 将来源中的全局消息导出到context 如果未提供context,则创建一个新字典 ''' if context is None: context = dict() _move(context, *get_all_message(source, with_alert, normalize)) return context
# 生成URL
[文档] def append_query(url: str, *, _query: str = '', **querys): ''' 在URL末尾附加GET参数 关键字参数 - _query: str, 直接用于拼接的字符串 说明 - 已包含GET参数的URL将会以`&`连接 - _query开头无需包含`?`或`&` - 基于字符串(`str`)拼接,URL不能包含段参数`#` ''' if not querys and not _query: return url concat = '&' if '?' in url else '?' new_querys = [] if _query: new_querys.append(_query.lstrip('?&')) for k, v in querys.items(): new_querys.append(f'{k}={v}') return url + concat + '&'.join(new_querys)
[文档] def message_url(context: dict | MESSAGECONTEXT, url: str = '/welcome/') -> str: ''' 提供要发送的信息体和原始URL,返回带提示信息的URL - context: 包含`warn_code`和`warn_message`的字典 - url: str, 可以包含GET参数 ''' warn_code, warn_message = context[CODE_FIELD], context[MSG_FIELD] # 如果不是以上的情况(即合法的字典), 就报错吧, 别静默发生错误 return append_query(url, warn_code=warn_code, warn_message=warn_message)
# 读取其它目录类内容
[文档] def read_key( content: dict, key: str, trans_func: Callable = None, default=None, raise_exception=False, ): ''' 读取键 - key: 待读取的键值 - default: 键不存在时的返回值, 默认为`None` - trans_func: 可选, 键存在时, 结果的类型转换函数, 如`int`, `str` - raise_exception: 键不存在时是否抛出异常, 默认不抛出 ''' try: result = content[key] return result if trans_func is None else trans_func(result) except Exception as e: if raise_exception: raise return default
[文档] def read_content( _content: dict, *_keys: str, _default=None, _trans_func: Callable = None, _raise: bool = False, _flat: bool = False, **_fields: Union[Callable, Any, Sequence], ) -> Union[dict, tuple]: ''' ### 读取目录 #### 读取字段 - _keys: 以默认设置读取的键值表 - _fields: 以自定义格式读取的键,值以如下顺序解读(未解析部分以默认值补齐): 1. 如果可调用,被视为_trans_func参数 2. 如果是字符类序列,被视为_default参数 3. 如果可切片,至多三个起始元素分别对应_default,_trans_func和_raise参数 4. 当元素不足3个且第一个参数可调用时,元素分别对应_trans_func和_raise参数 5. 如果不可切片,被视为_default参数 #### 设置 - _default: 键不存在时的返回值, 默认为`None` - _trans_func: 可选, 键存在时, 结果的类型转换函数, 如`int`, `str` - _raise: 键不存在时是否抛出异常, 默认不抛出 - _flat: 以值列表顺序输出结果,按照先keys后fields的顺序,默认以字典输出 #### 样例代码 ``` c = dict(a=1, b=2, d='msg') # 读取可选字段 >>> read_content(c, 'a', 'c', _trans_func=int) <<< {'a': 1, 'c': None} # 读取必要(类型匹配)字段 >>> read_content(c, 'd', _raise=True) <<< {'d': 'msg'} # 同时读取必要和可选字段并检查/转化(可选为主) >>> read_content( ... c, 'a', 'c', _default=0., _trans_func=float, ... d=(str, True), ... ) <<< {'a': 1.0, 'c': 0.0, 'd': 'msg'} >>> read_content( ... c, 'a', 'c', _default=0., _trans_func=float, ... d=(int, True), e=(None, None, True), ... ) <<< ValueError: invalid literal for int() with base 10: 'msg' # 同时读取必要和可选字段并检查/转化(必要为主) >>> read_content( ... c, 'a', _raise=True, ... b=float, d=str, ... c=('', str, False), ... ) <<< {'a': 1, 'b': 2.0, 'd': 'msg', 'c': ''} # 输出为序列 >>> read_content( ... c, 'a', _raise=True, ... b=float, d=str, ... c=('', str, False), ... ) <<< (1, 2.0, 'msg', '') ``` ''' result = [] if _flat else {} for key in _keys: value = read_key(_content, key, _trans_func, _default, _raise) result.append(value) if _flat else result.setdefault(key, value) for key, args in _fields.items(): if callable(args): value = read_key(_content, key, args, _default, _raise) elif isinstance(args, (str, bytes)): value = read_key(_content, key, _trans_func, args, _raise) else: try: args = list(args[:3]) except: args = [args] finally: if len(args) > 0 and len(args) < 3 and callable(args[0]): args = [_default] + args args = args + [_default, _trans_func, _raise][len(args):] value = read_key(_content, key, args[1], args[0], args[2]) result.append(value) if _flat else result.setdefault(key, value) return tuple(result) if _flat else result
[文档] def read_GET(request, key: str, trans_func=None, default=None, raise_exception=False): ''' 读取GET参数 - key: 待读取的键值 - default: 键不存在时的返回值, 默认为`None` - trans_func: 可选, 键存在时, 结果的类型转换函数, 如`int`, `str` - raise_exception: 键不存在时是否抛出异常, 默认不抛出 - 调用者保证`request`是一个请求,否则行为未定义 ''' return read_key(request.GET, key, trans_func, default, raise_exception)
[文档] def read_POST(request, key: str, trans_func=None, default=None, raise_exception=False): ''' 读取POST参数 - key: 待读取的键值 - default: 键不存在时的返回值, 默认为`None` - trans_func: 可选, 键存在时, 结果的类型转换函数, 如`int`, `str` - raise_exception: 键不存在时是否抛出异常, 默认不抛出 - 调用者保证`request`是一个请求,否则行为未定义 ''' return read_key(request.POST, key, trans_func, default, raise_exception)