1
0

io_ctrl.py 5.1 KB

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