io_ctrl.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. """
  2. 输入输出的底层实现函数
  3. """
  4. import logging
  5. from functools import wraps
  6. from .session import get_session_implement, CoroutineBasedSession, get_current_task_id, get_current_session
  7. from .utils import run_as_function, to_coroutine
  8. logger = logging.getLogger(__name__)
  9. def send_msg(cmd, spec=None):
  10. msg = dict(command=cmd, spec=spec, task_id=get_current_task_id())
  11. get_current_session().send_task_command(msg)
  12. def chose_impl(gen_func):
  13. @wraps(gen_func)
  14. def inner(*args, **kwargs):
  15. gen = gen_func(*args, **kwargs)
  16. if get_session_implement() == CoroutineBasedSession:
  17. return to_coroutine(gen)
  18. else:
  19. return run_as_function(gen)
  20. return inner
  21. @chose_impl
  22. def next_event():
  23. res = yield get_current_session().next_client_event()
  24. return res
  25. @chose_impl
  26. def single_input(item_spec, valid_func, preprocess_func):
  27. """
  28. Note: 鲁棒性在上层完成
  29. 将单个input构造成input_group,并获取返回值
  30. :param item_spec: 单个输入项的参数 'name' must in item_spec, 参数一定已经验证通过
  31. :param valid_func: Not None
  32. :param preprocess_func: Not None
  33. """
  34. if item_spec.get('name') is None: # single input
  35. item_spec['name'] = 'data'
  36. else: # as input_group item
  37. return dict(item_spec=item_spec, valid_func=valid_func, preprocess_func=preprocess_func)
  38. label = item_spec['label']
  39. name = item_spec['name']
  40. # todo 是否可以原地修改spec
  41. item_spec['label'] = ''
  42. item_spec.setdefault('auto_focus', True) # 如果没有设置autofocus参数,则开启参数 todo CHECKBOX, RADIO 特殊处理
  43. spec = dict(label=label, inputs=[item_spec])
  44. data = yield input_control(spec, {name: preprocess_func}, {name: valid_func})
  45. return data[name]
  46. @chose_impl
  47. def input_control(spec, preprocess_funcs, item_valid_funcs, form_valid_funcs=None):
  48. """
  49. 发送input命令,监听事件,验证输入项,返回结果
  50. :param spec:
  51. :param preprocess_funcs: keys 严格等于 spec中的name集合
  52. :param item_valid_funcs: keys 严格等于 spec中的name集合
  53. :param form_valid_funcs:
  54. :return:
  55. """
  56. send_msg('input_group', spec)
  57. data = yield input_event_handle(item_valid_funcs, form_valid_funcs, preprocess_funcs)
  58. send_msg('destroy_form')
  59. return data
  60. def check_item(name, data, valid_func, preprocess_func):
  61. try:
  62. data = preprocess_func(data)
  63. error_msg = valid_func(data)
  64. except Exception as e:
  65. logger.warning('Get %r in valid_func for name:"%s"', e, name)
  66. error_msg = '字段内容不合法'
  67. if error_msg is not None:
  68. send_msg('update_input', dict(target_name=name, attributes={
  69. 'valid_status': False,
  70. 'invalid_feedback': error_msg
  71. }))
  72. return False
  73. return True
  74. @chose_impl
  75. def input_event_handle(item_valid_funcs, form_valid_funcs, preprocess_funcs):
  76. """
  77. 根据提供的校验函数处理表单事件
  78. :param item_valid_funcs: map(name -> valid_func) valid_func 为 None 时,不进行验证
  79. valid_func: callback(data) -> error_msg or None
  80. :param form_valid_funcs: callback(data) -> (name, error_msg) or None
  81. :param preprocess_funcs:
  82. :return:
  83. """
  84. while True:
  85. event = yield next_event()
  86. event_name, event_data = event['event'], event['data']
  87. if event_name == 'input_event':
  88. input_event = event_data['event_name']
  89. if input_event == 'blur':
  90. onblur_name = event_data['name']
  91. check_item(onblur_name, event_data['value'], item_valid_funcs[onblur_name],
  92. preprocess_funcs[onblur_name])
  93. elif event_name == 'from_submit':
  94. all_valid = True
  95. # 调用输入项验证函数进行校验
  96. for name, valid_func in item_valid_funcs.items():
  97. if not check_item(name, event_data[name], valid_func, preprocess_funcs[name]):
  98. all_valid = False
  99. if all_valid: # todo 减少preprocess_funcs[name]调用次数
  100. data = {name: preprocess_funcs[name](val) for name, val in event_data.items()}
  101. # 调用表单验证函数进行校验
  102. if form_valid_funcs:
  103. v_res = form_valid_funcs(data)
  104. if v_res is not None:
  105. all_valid = False
  106. onblur_name, error_msg = v_res
  107. send_msg('update_input', dict(target_name=onblur_name, attributes={
  108. 'valid_status': False,
  109. 'invalid_feedback': error_msg
  110. }))
  111. if all_valid:
  112. break
  113. elif event_name == 'from_cancel':
  114. data = None
  115. break
  116. else:
  117. logger.warning("Unhandled Event: %s", event)
  118. return data
  119. def output_register_callback(callback, **options):
  120. task_id = get_current_session().register_callback(callback, **options)
  121. return task_id