Skip to content

Commit

Permalink
feat: auto hold session as needed
Browse files Browse the repository at this point in the history
  • Loading branch information
wang0618 committed Oct 3, 2021
1 parent 7512fa1 commit e9a2933
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 5 deletions.
4 changes: 4 additions & 0 deletions pywebio/session/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Session:
next_client_event
on_task_exception
register_callback
need_keep_alive
defer_call
Expand Down Expand Up @@ -159,6 +160,9 @@ def defer_call(self, func):
"""设置会话结束时调用的函数。可以用于资源清理。"""
self.deferred_functions.append(func)

def need_keep_alive(self) -> bool:
raise NotImplementedError


def get_session_info_from_headers(headers):
"""从Http请求头中获取会话信息
Expand Down
15 changes: 14 additions & 1 deletion pywebio/session/coroutinebased.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,22 @@ def __init__(self, target, session_info, on_task_command=None, on_session_close=

self._closed = False

self._need_keep_alive = False

# 当前会话未结束运行(已创建和正在运行的)的协程数量。当 _alive_coro_cnt 变为 0 时,会话结束。
self._alive_coro_cnt = 1

main_task = Task(target(), session=self, on_coro_stop=self._on_task_finish)
main_task = Task(self._start_main_task(target), session=self, on_coro_stop=self._on_task_finish)
self.coros[main_task.coro_id] = main_task

self._step_task(main_task)

async def _start_main_task(self, target):
await target()
if self.need_keep_alive():
from ..session import hold
await hold()

def _step_task(self, task, result=None):
asyncio.get_event_loop().call_soon_threadsafe(partial(task.step, result))

Expand Down Expand Up @@ -203,6 +211,8 @@ async def callback_coro():
callback_task.coro.send(None)
cls.get_current_session().coros[callback_task.coro_id] = callback_task

self._need_keep_alive = True

return callback_task.coro_id

def run_async(self, coro_obj):
Expand All @@ -227,6 +237,9 @@ async def run_asyncio_coroutine(self, coro_obj):
res = await WebIOFuture(coro=coro_obj)
return res

def need_keep_alive(self) -> bool:
return self._need_keep_alive


class TaskHandler:
"""The handler of coroutine task
Expand Down
17 changes: 13 additions & 4 deletions pywebio/session/threadbased.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,15 @@ def main_task(target):
for t in self.threads:
if t.is_alive() and t is not threading.current_thread():
t.join()
try:
self.send_task_command(dict(command='close_session'))
except SessionClosedException:
pass

if self.need_keep_alive():
from ..session import hold
hold()
else:
try:
self.send_task_command(dict(command='close_session'))
except SessionClosedException:
pass
self._trigger_close_event()
self.close()

Expand Down Expand Up @@ -281,6 +286,10 @@ def register_thread(self, t: threading.Thread):
event_mq = queue.Queue(maxsize=self.event_mq_maxsize) # 线程内的用户事件队列
self.task_mqs[self._get_task_id(t)] = event_mq

def need_keep_alive(self) -> bool:
# if callback thread is activated, then the session need to keep alive
return self.callback_thread is not None


class ScriptModeSession(ThreadBasedSession):
"""Script mode的会话实现"""
Expand Down
88 changes: 88 additions & 0 deletions test/1.basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,92 @@ def target():
template.basic_output()
template.background_output()

def check_item(data):
pass

input_group('Input group', [
input('Text', type=TEXT, datalist=['data-%s' % i for i in range(10)], name='text',
required=True, help_text='required=True', validate=check_item),
input('Number', type=NUMBER, value="42", name='number', validate=check_item),
input('Float', type=FLOAT, name='float', validate=check_item),
input('Password', type=PASSWORD, name='password', validate=check_item),

textarea('Textarea', rows=3, maxlength=20, name='textarea',
help_text='rows=3, maxlength=20', validate=check_item),

textarea('Code', name='code', code={
'lineNumbers': False,
'indentUnit': 2,
}, value='import something\n# Write your python code', validate=check_item),

select('select-multiple', [
{'label': '标签0,selected', 'value': '0', 'selected': True},
{'label': '标签1,disabled', 'value': '1', 'disabled': True},
('标签2,selected', '2', True),
('标签3', '3'),
('标签4,disabled', '4', False, True),
'标签5,selected',
], name='select-multiple', multiple=True, value=['标签5,selected'], required=True,
help_text='required至少选择一项', validate=check_item),

select('select', [
{'label': '标签0', 'value': '0', 'selected': False},
{'label': '标签1,disabled', 'value': '1', 'disabled': True},
('标签2', '2', False),
('标签3', '3'),
('标签4,disabled', '4', False, True),
'标签5,selected',
], name='select', value=['标签5,selected'], validate=check_item),

checkbox('checkbox-inline', [
{'label': '标签0,selected', 'value': '0', 'selected': False},
{'label': '标签1,disabled', 'value': '1', 'disabled': True},
('标签2,selected', '2', True),
('标签3', '3'),
('标签4,disabled', '4', False, True),
'标签5,selected',
], inline=True, name='checkbox-inline', value=['标签5,selected', '标签0', '标签0,selected'], validate=check_item),

checkbox('checkbox', [
{'label': '标签0,selected', 'value': '0', 'selected': True},
{'label': '标签1,disabled', 'value': '1', 'disabled': True},
('标签2,selected', '2', True),
('标签3', '3'),
('标签4,disabled', '4', False, True),
'标签5',
], name='checkbox', validate=check_item),

radio('radio-inline', [
{'label': '标签0', 'value': '0', 'selected': False},
{'label': '标签1,disabled', 'value': '1', 'disabled': True},
('标签2', '2', False),
('标签3', '3'),
('标签4,disabled', '4', False, True),
'标签5,selected',
], inline=True, name='radio-inline', value='标签5,selected', validate=check_item),

radio('radio', [
{'label': '标签0', 'value': '0', 'selected': False},
{'label': '标签1,disabled', 'value': '1', 'disabled': True},
('标签2', '2', False),
('标签3', '3'),
('标签4,disabled', '4', False, True),
'标签5,selected',
], inline=False, name='radio', value='标签5,selected', validate=check_item),

file_upload('file_upload', name='file_upload', max_size='10m'),

actions('actions', [
{'label': '提交', 'value': 'submit'},
('提交2', 'submit2'),
'提交3',
{'label': 'disabled', 'disabled': True},
('重置', 'reset', 'reset'),
{'label': '取消', 'type': 'cancel'},
], name='actions', help_text='actions'),

] )

run_as_function(template.basic_input())
actions(buttons=['Continue'])
template.background_input()
Expand All @@ -32,6 +118,8 @@ def test(server_proc: subprocess.Popen, browser: Chrome):
time.sleep(1)
template.save_output(browser, '1.basic.html')

browser.get('http://localhost:8080/') # to close current session

template.test_defer_call()


Expand Down

0 comments on commit e9a2933

Please sign in to comment.