首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用交互式编程创建像jupyter笔记本这样的应用程序?

如何使用交互式编程创建像jupyter笔记本这样的应用程序?
EN

Stack Overflow用户
提问于 2022-02-28 05:59:35
回答 1查看 189关注 0票数 1

现在,我想开发一个关于交互式编程的应用程序,就像jupyter笔记本一样,但是我对这个方面知之甚少,有人能告诉我一些方法或知识来开始开发这个应用程序吗?

EN

回答 1

Stack Overflow用户

发布于 2022-05-09 13:29:42

阶段:

application.

  • Create

  • 为您的创建一个虚拟环境,一个用于运行code.
  1. Implement的jupyter-cell的虚拟环境,这是在客户机-服务器基础上两个虚拟环境之间交互的机制。

服务器向客户端发送执行单元格。

客户端执行单元格并将结果返回给server.

  • Implement服务器端所需的图形界面.

两个虚拟环境之间的交互机制如下所示

server.py

代码语言:javascript
复制
# venv-1

import sys
from multiprocessing.connection import Listener, Connection


def read_write_function(conn_for_execution: Connection, conn_for_interrupting: Connection):
    try:
        while True:
            try:
                std, received_output = conn_for_execution.recv()
            except (ConnectionResetError, KeyboardInterrupt, EOFError) as e:
                print(e)
                break
            if std in ('<stderr>', '<stdout>'):
                file = sys.stderr if std == '<stderr>' else sys.stdout
                print('stream:', std)
                print('message:', repr(received_output)[1:-1], file=file)
            elif std == '<error>':  # error
                print('error:', repr(received_output)[1:-1], file=sys.stderr)
            elif std in ('<block>', '<read>', '<readlines>'):  # next block query or read input
                print('[Ctrl+C to send code block to client]')
                lines = []
                try:
                    while True:
                        line = input(std[1:] + ' ')
                        lines.append(line)
                except (KeyboardInterrupt, EOFError):
                    conn_for_execution.send('\n'.join(lines))
                    print(('' if lines else 'nothing ') + 'sended')
                    # --------------------- <!-- only to emulate "interrupt execution"
                    if lines and lines[-1] == '#interrupt':
                        print('[SERVER] Sleep before')
                        import time
                        time.sleep(3)
                        conn_for_interrupting.send('interrupt')
                        print('[SERVER] Interrupt message sended')
                    # --------------------- --> only to emulate "interrupt execution"
                    # --------------------- <!-- only to emulate "exit"
                    if lines and lines[-1] == '#exit':
                        print('[SERVER] Sleep before')
                        import time
                        time.sleep(3)
                        conn_for_interrupting.send('exit')
                        print('[SERVER] Exit message sended')
                    # --------------------- --> only to emulate "exit"
            elif std == '<readline>':
                print('[one line to send input data to client]')
                conn_for_execution.send(input(std[1:] + ' '))
                print(std[1:] + ' sended')
    except:
        __import__('traceback').print_exc()


ADDRESS = 'localhost'
PORT = 60000
PASS = 'secret'

print('#' * 42)
print('Address:', ADDRESS)
print('Port:', PORT)
print('Pass:', PASS)
print('#' * 42)
print('Waiting for a client...')

# --------------------- <!-- only to run the client app on the server side and prevent Ctrl+C crashes

"""
import signal
import subprocess
import os


def pre_exec():
    signal.signal(signal.SIGINT, signal.SIG_IGN)  # ignore CTRL+C signal in the new process


executable = [os.path.join(os.path.abspath('ClientSide'), 'venv', 'Scripts', 'python'), '-uBq', 'client.py',
              f'--address={ADDRESS}',
              f'--port={PORT}',
              f'--password={PASS}',
              stdin=subprocess.DEVNULL]
if sys.platform.startswith('win'):
    exec_process = subprocess.Popen(executable, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
else:
    exec_process = subprocess.Popen(executable, preexec_fn=pre_exec)
"""

# --------------------- --> only to run the client app on the server side and prevent Ctrl+C crashes


# backlog = 2 --> Two clients: one for executing code blocks and one for interrupting execution
try:
    with Listener((ADDRESS, PORT), authkey=PASS.encode(encoding='utf-8'), backlog=2) as listener, \
            listener.accept() as conn_for_execution, listener.accept() as conn_for_interrupting:
        print('Connections accepted')
        print('#' * 42)
        read_write_function(conn_for_execution, conn_for_interrupting)
except:
    pass

运行:

ServerSide/venv/Scripts/python -uB server.py

client.py

代码语言:javascript
复制
# venv-2

import argparse
import os
import sys
from _thread import get_native_id
from code import InteractiveInterpreter
from io import TextIOWrapper, BytesIO
from multiprocessing.connection import Client, Connection
from threading import Thread, Event

parser = argparse.ArgumentParser(prog='client.py')
parser.add_argument('--address', nargs='?', help='address ("localhost" by default)')
parser.add_argument('--port', nargs='?', help='port ("60000" by default)')
parser.add_argument('--password', nargs='?', help='password ("secret" by default)')
args = parser.parse_args()


if os.path.exists(__file__) and os.path.basename(__file__).startswith('tmp'):
    os.remove(__file__)


class Redirector(TextIOWrapper):
    def __init__(self, conn: Connection, std: TextIOWrapper):
        super().__init__(buffer=BytesIO(), encoding=std.encoding, errors=std.errors,
                         newline=std.newlines, line_buffering=std.line_buffering,
                         write_through=std.write_through)
        self.std = std
        self._conn = conn

    def read(self, size: int | None = None) -> str:
        try:
            self._conn.send(('<read>', 'read operation'))
            return self._conn.recv()
        except BaseException as e:
            print(e, file=sys.__stderr__)
            return ''

    def readline(self, size: int | None = None) -> str:
        try:
            self._conn.send(('<readline>', 'readline operation'))
            return self._conn.recv()
        except BaseException as e:
            print(e, file=sys.__stderr__)
            return ''

    def readlines(self, hint: int | None = None) -> list[str]:
        try:
            self._conn.send(('<readlines>', 'readlines operation'))
            return self._conn.recv().splitlines()
        except BaseException as e:
            print(e, file=sys.__stderr__)
            return []

    def write(self, data):
        try:
            self._conn.send((self.std.name, data))
        except BaseException as e:
            print(e, file=sys.__stderr__)

    def writelines(self, lines: list[str]):
        try:
            self._conn.send((self.std.name, os.linesep.join(lines)))
        except BaseException as e:
            print(e, file=sys.__stderr__)


class CodeBlocksInterpreter(InteractiveInterpreter):
    def __init__(self, conn_for_execution: Connection, conn_for_interrupting: Connection, locals: dict = None):
        super().__init__()
        self.locals = locals
        self._conn_for_execution = conn_for_execution
        self._conn_for_interrupting = conn_for_interrupting
        self._main_thread_id = get_native_id()
        self._ready_for_next_block = Event()
        self._ready_for_next_block.clear()
        self._can_interrupt = Event()
        self._can_interrupt.clear()
        self._thread = Thread(target=self._stop_and_exit_thread, daemon=False)

    def interact(self):
        self._thread.start()
        try:
            filename = '<input>'
            symbol = 'exec'
            while True:
                self._can_interrupt.clear()
                self._ready_for_next_block.wait()
                try:
                    self._conn_for_execution.send(('<block>', 'give me next block'))
                    code_block = self._conn_for_execution.recv() + '\n'
                    code = self.compile(source=code_block, filename=filename, symbol=symbol)
                    if code is None:
                        self.write('EOFError. Code block is incomplete')
                        continue
                    self._can_interrupt.set()
                    self.runcode(code)
                    self._can_interrupt.clear()
                except KeyboardInterrupt as e:
                    print(e, file=sys.__stderr__)
                except (OverflowError, SyntaxError, ValueError):
                    self.showsyntaxerror(filename)
                except SystemExit:
                    break
        except BaseException as e:
            print(e, file=sys.__stderr__)
        try:
            self._conn_for_execution.close()
        except:
            pass
        try:
            self._conn_for_interrupting.close()
        except:
            pass

    def _stop_and_exit_thread(self):
        try:
            while True:
                try:
                    self._ready_for_next_block.set()
                    received = self._conn_for_interrupting.recv()
                    if received == 'interrupt':
                        self._ready_for_next_block.clear()
                        if self._can_interrupt.is_set():
                            import ctypes
                            ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self._main_thread_id),
                                                                       ctypes.py_object(KeyboardInterrupt))
                    elif received == 'exit':
                        import ctypes
                        ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self._main_thread_id),
                                                                   ctypes.py_object(SystemExit))
                        break
                except (ConnectionResetError, EOFError):
                    break
        except BaseException as e:
            print(e, file=sys.__stderr__)

    def write(self, data: str):
        self._conn_for_execution.send(('<error>', data))


ADDRESS = args.address.strip('"\'') if isinstance(args.address, str) else 'localhost'
PORT = int(args.port) if isinstance(args.port, str) and args.port.isdigit() else 60000
PASS = args.password.strip('"\'').encode('utf-8') if isinstance(args.password, str) else b'secret'

# Two clients: one for executing code blocks and one for interrupting execution
try:
    with Client((ADDRESS, PORT), authkey=PASS) as conn_for_execution, \
            Client((ADDRESS, PORT), authkey=PASS) as conn_for_interrupting:
        sys.stdin = Redirector(conn_for_execution, sys.stdin)
        sys.stdout = Redirector(conn_for_execution, sys.stdout)
        sys.stderr = Redirector(conn_for_execution, sys.stderr)
        sys.__stdin__ = Redirector(conn_for_execution, sys.__stdin__)
        sys.__stdout__ = Redirector(conn_for_execution, sys.__stdout__)
        sys.__stderr__ = Redirector(conn_for_execution, sys.__stderr__)
        code_blocks_interpreter = CodeBlocksInterpreter(conn_for_execution, conn_for_interrupting,
                                                        locals={'__name__': '__main__'})
        code_blocks_interpreter.interact()
except:
    pass

if isinstance(sys.stdin, Redirector):
    sys.stdin = sys.stdin.std
if isinstance(sys.stdout, Redirector):
    sys.stdout = sys.stdout.std
if isinstance(sys.stderr, Redirector):
    sys.stderr = sys.stderr.std
if isinstance(sys.__stdin__, Redirector):
    sys.__stdin__ = sys.__stdin__.std
if isinstance(sys.__stdout__, Redirector):
    sys.__stdout__ = sys.__stdout__.std
if isinstance(sys.__stderr__, Redirector):
    sys.__stderr__ = sys.__stderr__.std

之后运行 server.py:

ClientSide/venv/Scripts/python -uB client.py

在服务器端,输入代码块并发送Ctrl+C。

在客户端,它被执行,结果被传送回服务器端。

示例:

  1. 打印到标准输出:

[Ctrl+C to send code block to client]

block> ``print(42 to stdout和stderr )引发异常:

[Ctrl+C to send code block to client]

block> import sys, time

block> print('1', file=sys.stdout); time.sleep(1)

block> print('2', file=sys.stderr); time.sleep(1)

block> raise Exception('3')

block> <Ctrl+C>

  • Read:

[Ctrl+C to send code block to client]

block> import sys

block> s1 = sys.stdin.read()

block> <Ctrl+C>

read> <Multi-line>

read> <Ctrl+C>

s2 = sys.stdin.readline() (或s2 = input()) )

block> <Ctrl+C>

readline> <One-line>

block> s3 = sys.stdin.readlines()

block> <Ctrl+C>

readlines> <Multi-line>

readlines> <Ctrl+C>

block> print(s1, s2, s3)

<Ctrl+C>

  • Interrupt (#interrupt必须是最后一行代码):

[Ctrl+C to send code block to client]

block> import time

block> for i in range(10):

block> print(i)

block> time.sleep(1)

block> #interrupt

block> <Ctrl+C>

[SERVER] Sleep before

[SERVER] Interrupt message sended

  • Exit (#exit必须是最后一行代码):

[Ctrl+C to send code block to client]

block> import time

block> for i in range(10):

block> print(i)

block> time.sleep(1)

block> #exit

block> <Ctrl+C>

[SERVER] Sleep before

[SERVER] Exit message sended

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71291111

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档