async def display(ic, idx): field = ic.md.display_fields[idx] if 'fn' in field: source = display_fn(ic, {'fname': 'pprint', 'args': [field['fn']]}) elif 'path' in field: source = display_fn(ic, {'fname': 'pprint', 'args': [field['path']]}) async with core.streamcontext(source) as streamer: async for item in streamer: yield {'type': 'display', 'id': idx, 'val': item}
async def display_fn(ic, field): try: m = getattr(fn, field['fname']) fnc = getattr(m, field['fname']) source = fnc(ic, field['args']) except AttributeError: source = docker( ic, [pathlib.Path("/lib") / field['fname'].replace('.', '/')] + field['args']) async with core.streamcontext(source) as streamer: async for item in streamer: yield item
async def input(ic, name): field = ic.md.input_fields[name] owner = ic.user restart = field['can_read'] while restart: restart = False src = [await arg_stream(ic, owner, pathlib.Path(field['name']))] if 'owner' in field['args']: src.append(await arg_stream(ic, ic.user, field['args']['owner'])) s = stream.ziplatest(*src, partial=False) last = None async with core.streamcontext(s) as streamer: async for i in streamer: if 'owner' in field['args']: if i[1] is None: continue o = i[1]['val'] if o.pk != owner.pk: owner = o restart = True break out = dict(type='input', id=field['name'], disabled=True) out['owner'] = None if ic.user == owner else owner.username out['val'] = '' if i[0] is None or i[0]['val'] is None else str( i[0]['val']) out['disabled'] = not (field['can_write'] is True or (field['can_write'] is None and out['owner'] is None)) if last == out: continue last = out if field['args']['type'] in ['file', 'files', 'select-user']: out['val'] = None if i[0] is not None and field['args']['type'] != i[0]['type']: logger.warning( f"field type error: {field['args']['type']} != {i[0]['type']}" ) yield out
async def docker(ic, args): if len(args) < 1: yield None return if not isinstance(args[0], pathlib.Path): yield ('err', "⚠ wrong first argument format ⚠") return docker_path = pathlib.Path(os.path.normpath(ic.path / args[0])) dapi = None try: dapi = aiodocker.Docker() con = await get_container(dapi, docker_path, ic.user) if con is None: return logger.info(f"{con['id'][:12]}: created") ain = dict() aout = dict() for n, arg in enumerate(args[1:]): ain[n + 1] = stream_enum( n + 1, await my_stream.arg_stream(ic, ic.user, arg)) ws = await con.websocket(stdin=True, stdout=True, stderr=True, stream=True) await con.start() logger.debug(f"{con['id'][:12]}: started") msgs_n = 0 msgs_ts = time.time() try: restart = True while restart: restart = False if ain: s = stream.merge(*list(ain.values()), websocket_reader(ws)) else: s = stream.merge(websocket_reader(ws)) # if input is empty, send empty message to container logger.debug(f"{con['id'][:12]}: < []") await ws.send_str(json.dumps(list()) + "\n") out = list() async with core.streamcontext(s) as streamer: async for item in streamer: if item[0] == 'ws': logger.debug( f"{con['id'][:12]}: > {item[1][:120]}") msgs_n += 1 tdiff = time.time() - msgs_ts if tdiff > 20 and msgs_n > 1000 and msgs_n / tdiff > 20: logger.info( f"{con['id'][:12]}: receiving too much messages {msgs_n} in {tdiff}s" ) yield { 'type': 'error', 'val': f"receiving too much messages {msgs_n} in {tdiff}s" } break m = wi_native_re.match(item[1]) if m: if m.group(1) == 'clear': out = list() yield {'type': None, 'val': ""} elif m.group(1).startswith('progress'): yield { 'type': 'html', 'val': '<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%"></div></div>' } else: try: msg = json.loads(m.group(1)) if msg.get('type') in ['getval']: mid = msg.get('id') mval = msg.get('val') muser = msg.get('user') if muser is None: muser = ic.user.pk if mid is None or mval is None: logger.warning( f"{con['id'][:12]}: getval: broken msg: > {item[1][:120]}" ) continue if mid in ain: continue try: u = User.objects.get(pk=muser) except User.DoesNotExist: logger.warning( f"{con['id'][:12]}: getval: unknown user {muser}" ) continue arg = pathlib.Path(mval) ain[mid] = stream_enum( mid, await my_stream.arg_stream( ic, u, arg)) restart = True break elif msg['type'] in ['error']: yield msg break else: yield msg except json.JSONDecodeError as e: logger.warning( f"{con['id'][:12]}: broken msg: {e!s}" ) continue else: out.append(item[1]) yield {'type': 'stdout', 'val': "\n".join(out)} elif item[0] == 'err': logger.debug(f"{con['id'][:12]}: !! " + ' '.join(str(item[1]).split())[:120]) yield {'type': 'error', 'val': item[1]} break else: await send_item(ws, con, aout, item) except GeneratorExit: pass finally: logger.debug(f"{con['id'][:12]}: delete") try: await con.kill() await con.delete() pass except Exception: pass ws.close() except MyException as e: yield {'type': 'error', 'val': f"⚠ {e!s} ⚠"} except aiodocker.exceptions.DockerError as e: yield {'type': 'error', 'val': f"⚠ {e!s} ⚠"} except Exception as e: logger.exception(e) yield {'type': 'error', 'val': f"⚠ {e!s} ⚠"} finally: if dapi: await dapi.close()
async def stream_enum(n, s): async with core.streamcontext(s) as streamer: async for i in streamer: yield (n, i)
async def html_escape(source, user, path): async with core.streamcontext(source) as streamer: async for item in streamer: s = " ".join([str(x) for x in item if x is not None]) yield html.escape(s).replace('\n', "<br />")
async def pprint(ic, args): a = [await my_stream.arg_stream(ic, ic.user, arg) for arg in args] source = stream.ziplatest(*a, partial=False) async with core.streamcontext(source) as streamer: async for item in streamer: keys = list() vals = dict() info = list() for i, val in enumerate(item): if val is None: continue if val.get('pk') is not None: info.append(await get_history(val['pk'])) if val['type'] is None: pass elif val['type'] in ['html']: if None not in keys: keys.insert(0, None) vals[None] = [None] * len(item) vals[None][i] = val['val'] elif val['type'] in [ 'int', 'float', 'str', 'text', 'textarea', 'files', 'stdout', 'error' ]: if None not in keys: keys.insert(0, None) vals[None] = [None] * len(item) vals[None][i] = render_to_string( f"wiki/plugins/inputs/pprint.html", context=val) elif val['type'] == 'user-list': for u, v in val['val'].items(): if u not in keys: keys.append(u) vals[u] = [None] * len(item) vals[u][i] = render_to_string( f"wiki/plugins/inputs/pprint.html", context=v) else: logger.error(val) if len(keys) == 0: yield None continue if len(keys) == 1 and keys[0] is None: html = " ".join(vals[keys[0]]) else: html = "<table>" for k in keys: html += "<tr><th>" if k is None: html += " " elif isinstance(k, User): html += f"{k.first_name} {k.last_name}" else: html += str(k) html += "</th>" for v in vals[k]: html += f"<td>{v!s}</td>" html += "</tr>" html += "</table>" if info: html = render_to_string("wiki/plugins/inputs/pprint_full.html", context={ 'html': html, 'info': "".join(info) }) yield {'type': 'html', 'val': html}
async def get(ic, args): if len(args) < 2: yield { 'type': 'error', 'val': "⚠ get() requires at least 2 arguments ⚠" } return if not isinstance(args[0], pathlib.Path): yield {'type': 'error', 'val': "⚠ get() first argument must be path ⚠"} return path = misc.normpath(ic, args[0]) md = await misc.get_markdown_factory().get_markdown(path.parent, ic.user) if md is None: yield { 'type': 'error', 'val': f"⚠ get() article {path.parent} does not exist ⚠" } return field = md.input_fields.get(path.name) if field is None: yield { 'type': 'error', 'val': f"⚠ get() article {path.name} does not exist ⚠" } q = Q(article=md.article) & Q(name=field['name']) if field['can_write'] in [False, None]: q &= Q(owner=ic.user) users = set() while True: src = [my_stream.read_field(ic, x, path) for x in users] src += [await my_stream.arg_stream(ic, ic.user, x) for x in args[1:]] if len(users) == 0: src += [my_stream.read_field(ic, ic.user, path)] s = stream.ziplatest(*src, partial=False) async with core.streamcontext(s) as streamer: async for i in streamer: users_new, is_list = await db_get_input_users( md, field, q, (i[len(users):])[:len(args[1:])]) if users_new is None: continue if set([u.pk for u in users]) != set([u.pk for u in users_new]): users = users_new break if is_list: yield { 'type': 'user-list', 'val': dict(zip([u.username for u in users], i[:len(users)])), } elif len(users) > 0: yield i[0] else: yield {'type': 'None', 'val': None} else: return