関数 - python3 process



リアルタイム入力と複数のコンソールを持つPythonサブプロセス (1)

あなたが直面している問題は、Windows上のコンソールサブシステムのアーキテクチャです。通常見られるコンソールウィンドウはcmd.exeによってホストされているのではなく、代わりにconhost.exeによってホストされています。単一のコンホストインスタンスは、プロセスごとに単一のウィンドウに制限されていることを意味します。

これにより、各コンソールウィンドウに追加のプロセスが必要になります。そのウィンドウで何かを表示するには、stdinとstdoutが通常どのように処理されるかを確認する必要があります。 stdinをパイプに変換する場合(つまりプロセスに書き込めるようにする場合)を除いて、conhostインスタンスによるものです。これはもはやconhostからではなく、代わりに親プロセスからのものであるため、conhostには表示されません。 つまり、stdinに書き込まれたものはすべて子プロセスによって読み取られるだけなので、conhostでは表示されません。

私の知る限りでは、そのようなパイプを共有する方法はありません。

副作用として、標準入力をパイプにすると、標準入力はそのウィンドウに接続されていないので、新しいコンソールウィンドウに送信されるすべてのキーボード入力はどこにも行き来しません。

出力専用関数の場合、これはパイプを介して親と通信してstdinし、すべてをstdoutにエコーする新しいプロセスを生成できることを意味します。

試みがあります。

#!python3

import sys, subprocess, time

class Console():
    def __init__(self):
        if '-r' not in sys.argv:
            self.p = subprocess.Popen(
                ['python.exe', __file__, '-r'],
                stdin=subprocess.PIPE,
                creationflags=subprocess.CREATE_NEW_CONSOLE
                )
        else:
            while True:
                data = sys.stdin.read(1)
                if not data:
                    break
                sys.stdout.write(data)

    def write(self, data):
        self.p.stdin.write(data.encode('utf8'))
        self.p.stdin.flush()

if (__name__ == '__main__'):
    p = Console()
    if '-r' not in sys.argv:
        for i in range(0, 100):
            p.write('test %i\n' % i)
            time.sleep(1)

そのため、2つのプロセス間の素敵な単純なパイプで、そのサブプロセスの場合は入力を出力にエコーバックします。インスタンスがプロセスかどうかを示すのに-rを使用しましたが、実装方法によって他の方法があります。

注意することがいくつかあります。

  • Pythonは通常バッファリングを使用しているので、標準入力への書き込み後のフラッシュが必要です。
  • このアプローチが書かれている方法は、それ自身のモジュールにあることを目的としているので、 __file__使用し__file__
  • __file__使用しているため、cx_Freezeなどを使用してフリーズした場合は、この方法を変更する必要があります。

編集1

cx_Freezeでフリーズできるバージョンの場合:

Console.py

import sys, subprocess

class Console():
    def __init__(self, ischild=True):
        if not ischild:
            if hasattr(sys, 'frozen'):
                args = ['Console.exe']
            else:
                args = [sys.executable, __file__]
            self.p = subprocess.Popen(
                args,
                stdin=subprocess.PIPE,
                creationflags=subprocess.CREATE_NEW_CONSOLE
                )
        else:
            while True:
                data = sys.stdin.read(1)
                if not data:
                    break
                sys.stdout.write(data)

    def write(self, data):
        self.p.stdin.write(data.encode('utf8'))
        self.p.stdin.flush()

if (__name__ == '__main__'):
    p = Console()

test.py

from Console import Console
import sys, time

if (__name__ == '__main__'):
    p = Console(False)
    for i in range(0, 100):
        p.write('test %i\n' % i)
        time.sleep(1)

setup.py

from cx_Freeze import setup, Executable

setup(
    name = 'Console-test',
    executables = [
        Executable(
            'Console.py',
            base=None,
            ),
        Executable(
            'test.py',
            base=None,
            )
        ]
)

編集2

IDLEのような開発ツールの下で動作するはずの新しいバージョン

Console.py

#!python3

import ctypes, sys, subprocess

Kernel32 = ctypes.windll.Kernel32

class Console():
    def __init__(self, ischild=True):
        if ischild:
            # try allocate new console
            result = Kernel32.AllocConsole()
            if result > 0:
                # if we succeed open handle to the console output
                sys.stdout = open('CONOUT$', mode='w')
        else:
            # if frozen we assume its names Console.exe
            # note that when frozen 'Win32GUI' must be used as a base
            if hasattr(sys, 'frozen'):
                args = ['Console.exe']
            else:
                # otherwise we use the console free version of python
                args = ['pythonw.exe', __file__]
            self.p = subprocess.Popen(
                args,
                stdin=subprocess.PIPE
                )
            return
        while True:
            data = sys.stdin.read(1)
            if not data:
                break
            sys.stdout.write(data)

    def write(self, data):
        self.p.stdin.write(data.encode('utf8'))
        self.p.stdin.flush()

if (__name__ == '__main__'):
    p = Console()

test.py

from Console import Console
import sys, time

if (__name__ == '__main__'):
    p = Console(False)
    for i in range(0, 100):
        p.write('test %i\n' % i)
        time.sleep(1)

setup.py

from cx_Freeze import setup, Executable

setup(
    name = 'Console-test',
    executables = [
        Executable(
            'Console.py',
            base='Win32GUI',
            ),
        Executable(
            'test.py',
            base=None,
            )
        ]
)

これは、より堅牢にすることができます。つまり、新しいコンソールを作成する前に既存のコンソールを常に確認し、見つかった場合はそれをデタッチすることで、エラー処理を改善することができます。

主な問題

一言で言えば:私は私のプログラムに2つのコンソールが欲しいのです。 アクティブなユーザー入力用です。 もう1つは純粋なログ出力です。 (受け入れられた回答を含む作業コードは、下のセクション「編集-3」の下にあります。セクション「編集-1」およびセクション「編集-2」の下には、機能する回避策があります。)

このために、ログ出力専用に追加のコンソールを開くことになっているメインコマンドラインのPythonスクリプトがあります。 このために、メインスクリプトのコンソールに出力されるログ出力を、サブプロセスとして起動する2番目のコンソールの標準入力にリダイレクトします。 (私はサブプロセスを使用します、なぜなら私は他の方法で2番目のコンソールを開く方法を見つけられなかったからです。)

問題はそれが私がこの2番目のコンソールのstdinに送ることができるように思われるということです - しかし、この2番目のコンソールには何も表示されません。

次に示すのは、私が実験に使用したコードです(Windows 10でPyDev上のPython 3.4を使用)。 関数writing(input, pipe, process)には、生成された文字列が、viaサブプロセスによって開かれたコンソールの、stdinに渡されたas pipeコピーされる部分が含まれています。 関数writing(...)はクラスwritetest(Thread)介して実行されます。 (私はコメントアウトしたいくつかのコードを残しました。)

import os
import sys
import io
import time
import threading
from cmd import Cmd
from queue import Queue
from subprocess import Popen, PIPE, CREATE_NEW_CONSOLE


REPETITIONS = 3


# Position of "The class" (Edit-2)


# Position of "The class" (Edit-1)


class generatetest(threading.Thread):

    def __init__(self, queue):
        self.output = queue
        threading.Thread.__init__(self)

    def run(self):
        print('run generatetest')
        generating(REPETITIONS, self.output)
        print('generatetest done')

    def getout(self):
        return self.output


class writetest(threading.Thread):

    def __init__(self, input=None, pipe=None, process=None):
        if (input == None):        # just in case
            self.input = Queue()
        else:
            self.input = input

        if (pipe == None):        # just in case
            self.pipe = PIPE
        else:
            self.pipe = pipe

        if (process == None):        # just in case
            self.process = subprocess.Popen('C:\Windows\System32\cmd.exe', universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
        else:
            self.process = proc

        threading.Thread.__init__(self)

    def run(self):
        print('run writetest')
        writing(self.input, self.pipe, self.process)
        print('writetest done')


# Position of "The function" (Edit-2)


# Position of "The function" (Edit-1)


def generating(maxint, outline):
    print('def generating')
    for i in range(maxint):
        time.sleep(1)
        outline.put_nowait(i)


def writing(input, pipe, process):
    print('def writing')
    while(True):
        try:
            print('try')
            string = str(input.get(True, REPETITIONS)) + "\n"
            pipe = io.StringIO(string)
            pipe.flush()
            time.sleep(1)
            # print(pipe.readline())
        except:
            print('except')
            break
        finally:
            print('finally')
            pass


data_queue = Queue()
data_pipe = sys.stdin
# printer = sys.stdout
# data_pipe = os.pipe()[1]


# The code of 'C:\\Users\\Public\\Documents\\test\\test-cmd.py'
# can be found in the question's text further below under "More code"


exe = 'C:\Python34\python.exe'
# exe = 'C:\Windows\System32\cmd.exe'
arg = 'C:\\Users\\Public\\Documents\\test\\test-cmd.py'
arguments = [exe, arg]
# proc = Popen(arguments, universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
proc = Popen(arguments, stdin=data_pipe, stdout=PIPE, stderr=PIPE,
             universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)


# Position of "The call" (Edit-2 & Edit-1) - file init (proxyfile)


# Position of "The call" (Edit-2) - thread = sockettest()
# Position of "The call" (Edit-1) - thread0 = logtest()
thread1 = generatetest(data_queue)
thread2 = writetest(data_queue, data_pipe, proc)
# time.sleep(5)


# Position of "The call" (Edit-2) - thread.start()
# Position of "The call" (Edit-1) - thread0.start()
thread1.start()
thread2.start()


# Position of "The call" (Edit-2) - thread.join()
# Position of "The call" (Edit-1) - thread.join()
thread1.join(REPETITIONS * REPETITIONS)
thread2.join(REPETITIONS * REPETITIONS)

# data_queue.join()
# receiver = proc.communicate(stdin, 5)
# print('OUT:' + receiver[0])
# print('ERR:' + receiver[1])

print("1st part finished")

少し異なるアプローチ

次の追加のコードスニペットは、サブプロセスから標準出力を抽出することに関して機能します。 ただし、以前に送信されたstdinはまだ2番目のコンソールには表示されません。 また、2番目のコンソールはすぐに閉じられます。

proc2 = Popen(['C:\Python34\python.exe', '-i'],
              stdin=PIPE,
              stdout=PIPE,
              stderr=PIPE,
              creationflags=CREATE_NEW_CONSOLE)
proc2.stdin.write(b'2+2\n')
proc2.stdin.flush()
print(proc2.stdout.readline())
proc2.stdin.write(b'len("foobar")\n')
proc2.stdin.flush()
print(proc2.stdout.readline())
time.sleep(1)
proc2.stdin.close()
proc2.terminate()
proc2.wait(timeout=0.2)

print("Exiting Main Thread")

より詳しい情報

サブプロセスを開始するためにstdin=data_pipe, stdout=PIPE, stderr=PIPEいずれかを使用するとすぐに、表示される2番目のコンソールはアクティブにならず、キーボード入力も受け付けません(望ましいとは言えませんが)。ここで役に立つ情報)。

プロセスが終了するのを待つので、サブプロセスメソッドcommunicate()はこれには使用できません。

より多くのコード

最後のファイルのコード、これは2番目のコンソール用です。

C:\ Users \ Public \ Documents \ test \ test-cmd.py

from cmd import Cmd
from time import sleep
from datetime import datetime

INTRO = 'command line'
PROMPT = '> '


class CommandLine(Cmd):
    """Custom console"""

    def __init__(self, intro=INTRO, prompt=PROMPT):
        Cmd.__init__(self)
        self.intro = intro
        self.prompt = prompt
        self.doc_header = intro
        self.running = False

    def do_dummy(self, args):
        """Runs a dummy method."""
        print("Do the dummy.")
        self.running = True
        while(self.running == True):
            print(datetime.now())
            sleep(5)

    def do_stop(self, args):
        """Stops the dummy method."""
        print("Stop the dummy, if you can.")
        self.running = False

    def do_exit(self, args):
        """Exits this console."""
        print("Do console exit.")
        exit()

if __name__ == '__main__':
    cl = CommandLine()
    cl.prompt = PROMPT
    cl.cmdloop(INTRO)

考え

これまでのところ私は、Windowsのコマンドラインインターフェースがキーボードからの入力以外の入力を受け付ける機能を提供しているかどうかさえもはっきりしていません(望ましい標準入力パイプなどの代わりに)。 それでも、ある種のパッシブモードがあるので、それを期待しています。

なぜこれが機能しないのですか?

編集-1:ファイルによる回避策(概念実証)

python複数のコンソールを操作するの回答で示唆されているように、ファイルを表示するための回避策としてファイルを使用することは一般的に機能しています。 ただし、ログファイルは最大数GBまで増大するため、この場合は実用的な解決策ではありません。 それは少なくともファイル分割とそれの正しい取り扱いを必要とするでしょう。

クラス:

class logtest(threading.Thread):

    def __init__(self, file):
        self.file = file
        threading.Thread.__init__(self)

    def run(self):
        print('run logtest')
        logging(self.file)
        print('logtest done')

関数:

def logging(file):
    pexe = 'C:\Python34\python.exe '
    script = 'C:\\Users\\Public\\Documents\\test\\test-004.py'
    filek = '--file'
    filev = file

    file = open(file, 'a')
    file.close()
    time.sleep(1)

    print('LOG START (outer): ' + script + ' ' + filek + ' ' + filev)
    proc = Popen([pexe, script, filek, filev], universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)
    print('LOG FINISH (outer): ' + script + ' ' + filek + ' ' + filev)

    time.sleep(2)

呼び出し:

# The file tempdata is filled with several strings of "0\n1\n2\n"
# Looking like this:
# 0
# 1
# 2
# 0
# 1
# 2

proxyfile = 'C:\\Users\\Public\\Documents\\test\\tempdata'
f = open(proxyfile, 'a')
f.close()
time.sleep(1)

thread0 = logtest(proxyfile)
thread0.start()
thread0.join(REPETITIONS * REPETITIONS)

末尾のスクリプト( "test-004.py"):

Windowsはtailコマンドを提供していないので、代わりに次のスクリプトを使用しました( tail -Fと同等のpythonicを実装する方法の答えに基づく )。 追加ではあるが一種の不要なclass CommandLine(Cmd)は、最初は2番目のコンソールを開いたままにしようとする試みでした(スクリプトファイルの引数がなかったため)。 とはいえ、それはコンソールが新しいログファイルの内容を流暢に出力し続けるためにも有用であることが証明されました。 そうでなければ、出力は決定論的/予測可能ではありませんでした。

import time
import sys
import os
import threading
from cmd import Cmd
from argparse import ArgumentParser


def main(args):
    parser = ArgumentParser(description="Parse arguments.")
    parser.add_argument("-f", "--file", type=str, default='', required=False)
    arguments = parser.parse_args(args)

    if not arguments.file:
        print('LOG PRE-START (inner): file argument not found. Creating new default entry.')
        arguments.file = 'C:\\Users\\Public\\Documents\\test\\tempdata'

    print('LOG START (inner): ' + os.path.abspath(os.path.dirname(__file__)) + ' ' + arguments.file)

    f = open(arguments.file, 'a')
    f.close()
    time.sleep(1)

    words = ['word']
    console = CommandLine(arguments.file, words)
    console.prompt = ''

    thread = threading.Thread(target=console.cmdloop, args=('', ))
    thread.start()
    print("\n")

    for hit_word, hit_sentence in console.watch():
        print("Found %r in line: %r" % (hit_word, hit_sentence))

    print('LOG FINISH (inner): ' + os.path.abspath(os.path.dirname(__file__)) + ' ' + arguments.file)


class CommandLine(Cmd):
    """Custom console"""

    def __init__(self, fn, words):
        Cmd.__init__(self)
        self.fn = fn
        self.words = words

    def watch(self):
        fp = open(self.fn, 'r')
        while True:
            time.sleep(0.05)
            new = fp.readline()
            print(new)
            # Once all lines are read this just returns ''
            # until the file changes and a new line appears

            if new:
                for word in self.words:
                    if word in new:
                        yield (word, new)

            else:
                time.sleep(0.5)


if __name__ == '__main__':
    print('LOG START (inner - as main).')
    main(sys.argv[1:])

編集-1:より多くの考え

私がまだ試していないし、うまくいくかもしれない3つの回避策はソケット(この回答でも提案されていますpythonで複数のコンソールを操作する )、より制御するためのプロセスIDによるプロセスオブジェクトの取得、そして直接Windowsにアクセスするためのctypesライブラリの使用コンソールAPIは、複数のバッファを持つことができますが、アクティブバッファは1つしかないため、画面バッファを設定できます( CreateConsoleScreenBuffer関数のドキュメントの備考に記載)。

ただし、ソケットを使用するのが最も簡単な方法です。 そして少なくともログのサイズはこの方法では関係ありません。 ただし、ここでは接続の問題が問題になる可能性があります。

編集2:ソケットによる回避策(概念実証)

新しいログエンティティーを表示するための回避策としてのソケットの使用は、pythonでの複数コンソール作業の回答でも示唆されているように、一般的にも機能しています。 しかし、これは何かのためには多すぎる努力のように思えます、それは単に受信側コンソールのプロセスに送られるべきです。

クラス:

class sockettest(threading.Thread):

    def __init__(self, host, port, file):
        self.host = host
        self.port = port
        self.file = file
        threading.Thread.__init__(self)

    def run(self):
        print('run sockettest')
        socketing(self.host, self.port, self.file)
        print('sockettest done')

関数:

def socketing(host, port, file):
    pexe = 'C:\Python34\python.exe '
    script = 'C:\\Users\\Public\\Documents\\test\test-005.py'
    hostk = '--address'
    hostv = str(host)
    portk = '--port'
    portv = str(port)
    filek = '--file'
    filev = file

    file = open(file, 'a')
    file.close()
    time.sleep(1)

    print('HOST START (outer): ' + pexe + script + ' ' + hostk + ' ' + hostv + ' ' + portk + ' ' + portv + ' ' + filek + ' ' + filev)
    proc = Popen([pexe, script, hostk, hostv, portk, portv, filek, filev], universal_newlines=True, creationflags=CREATE_NEW_CONSOLE)

    print('HOST FINISH (outer): ' + pexe + script + ' ' + hostk + ' ' + hostv + ' ' + portk + ' ' + portv + ' ' + filek + ' ' + filev)

    time.sleep(2)

呼び出し:

# The file tempdata is filled with several strings of "0\n1\n2\n"
# Looking like this:
# 0
# 1
# 2
# 0
# 1
# 2

proxyfile = 'C:\\Users\\Public\\Documents\\test\\tempdata'
f = open(proxyfile, 'a')
f.close()
time.sleep(1)

thread = sockettest('127.0.0.1', 8888, proxyfile)
thread.start()
thread.join(REPETITIONS * REPETITIONS)

ソケットスクリプト( "test-005.py"):

次のスクリプトはPythonを基にしていますスレッドを使ったソケットプログラミングサーバー - クライアントアプリケーション 。 ここでは、ログエントリジェネレータとしてclass CommandLine(Cmd)保持しています。 この時点で、2番目のコンソールを呼び出して(新しい)ファイル行の代わりに実際のログをキューに入れるメインスクリプトにクライアントを入れることは問題にならないはずです。 (サーバーはプリンターです。)

import socket
import sys
import threading
import time
from cmd import Cmd
from argparse import ArgumentParser
from queue import Queue

BUFFER_SIZE = 5120

class CommandLine(Cmd):
    """Custom console"""

    def __init__(self, fn, words, queue):
        Cmd.__init__(self)
        self.fn = fn
        self.words = words
        self.queue = queue

    def watch(self):
        fp = open(self.fn, 'r')
        while True:
            time.sleep(0.05)
            new = fp.readline()

            # Once all lines are read this just returns ''
            # until the file changes and a new line appears
            self.queue.put_nowait(new)


def main(args):
    parser = ArgumentParser(description="Parse arguments.")
    parser.add_argument("-a", "--address", type=str, default='127.0.0.1', required=False)
    parser.add_argument("-p", "--port", type=str, default='8888', required=False)
    parser.add_argument("-f", "--file", type=str, default='', required=False)
    arguments = parser.parse_args(args)

    if not arguments.address:
        print('HOST PRE-START (inner): host argument not found. Creating new default entry.')
        arguments.host = '127.0.0.1'
    if not arguments.port:
        print('HOST PRE-START (inner): port argument not found. Creating new default entry.')
        arguments.port = '8888'
    if not arguments.file:
        print('HOST PRE-START (inner): file argument not found. Creating new default entry.')
        arguments.file = 'C:\\Users\\Public\\Documents\\test\\tempdata'

    file_queue = Queue()

    print('HOST START (inner): ' + ' ' + arguments.address + ':' + arguments.port + ' --file ' + arguments.file)

    # Start server
    thread = threading.Thread(target=start_server, args=(arguments.address, arguments.port, ))
    thread.start()
    time.sleep(1)

    # Start client
    thread = threading.Thread(target=start_client, args=(arguments.address, arguments.port, file_queue, ))
    thread.start()

    # Start file reader
    f = open(arguments.file, 'a')
    f.close()
    time.sleep(1)

    words = ['word']
    console = CommandLine(arguments.file, words, file_queue)
    console.prompt = ''

    thread = threading.Thread(target=console.cmdloop, args=('', ))
    thread.start()
    print("\n")

    for hit_word, hit_sentence in console.watch():
        print("Found %r in line: %r" % (hit_word, hit_sentence))

    print('HOST FINISH (inner): ' + ' ' + arguments.address + ':' + arguments.port)


def start_client(host, port, queue):
    host = host
    port = int(port)         # arbitrary non-privileged port
    queue = queue

    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        soc.connect((host, port))
    except:
        print("Client connection error" + str(sys.exc_info()))
        sys.exit()

    print("Enter 'quit' to exit")
    message = ""

    while message != 'quit':
        time.sleep(0.05)
        if(message != ""):
            soc.sendall(message.encode("utf8"))
            if soc.recv(BUFFER_SIZE).decode("utf8") == "-":
                pass        # null operation

        string = ""
        if (not queue.empty()):
            string = str(queue.get_nowait()) + "\n"

        if(string == None or string == ""):
            message = ""
        else:
            message = string

    soc.send(b'--quit--')


def start_server(host, port):
    host = host
    port = int(port)         # arbitrary non-privileged port

    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # SO_REUSEADDR flag tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire
    soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    print("Socket created")

    try:
        soc.bind((host, port))
    except:
        print("Bind failed. Error : " + str(sys.exc_info()))
        sys.exit()

    soc.listen(5)       # queue up to 5 requests
    print("Socket now listening")

    # infinite loop- do not reset for every requests
    while True:
        connection, address = soc.accept()
        ip, port = str(address[0]), str(address[1])
        print("Connected with " + ip + ":" + port)

        try:
            threading.Thread(target=client_thread, args=(connection, ip, port)).start()
        except:
            print("Thread did not start.")
            traceback.print_exc()

    soc.close()


def client_thread(connection, ip, port, max_buffer_size=BUFFER_SIZE):
    is_active = True

    while is_active:
        client_input = receive_input(connection, max_buffer_size)

        if "--QUIT--" in client_input:
            print("Client is requesting to quit")
            connection.close()
            print("Connection " + ip + ":" + port + " closed")
            is_active = False
        elif not client_input == "":
            print("{}".format(client_input))
            connection.sendall("-".encode("utf8"))
        else:
            connection.sendall("-".encode("utf8"))


def receive_input(connection, max_buffer_size):
    client_input = connection.recv(max_buffer_size)
    client_input_size = sys.getsizeof(client_input)

    if client_input_size > max_buffer_size:
        print("The input size is greater than expected {}".format(client_input_size))

    decoded_input = client_input.decode("utf8").rstrip()  # decode and strip end of line
    result = process_input(decoded_input)

    return result


def process_input(input_str):
    return str(input_str).upper()


if __name__ == '__main__':
    print('HOST START (inner - as main).')
    main(sys.argv[1:])

編集-2:さらに考え

サブプロセスのコンソール入力パイプ/バッファを直接制御することは、この問題に対する望ましい解決策です。 これは500評判の恩恵です。

残念ながら私は時間切れです。 そのため、当面はこれらの回避策の1つを使用し、後でそれらを適切な解決策に置き換えることができます。 あるいは、核となるオプションを使用する必要があります。コンソールを1つだけ使用し、進行中のログ出力がユーザーのキーボード入力中に一時停止され、その後印刷されるようにします。 もちろん、ユーザーが途中で何かを入力することにした場合、これはバッファの問題につながる可能性があります。

編集-3:受け入れた解答を含むコード(1ファイル)

James Kentからの回答を得て、Windowsコマンドライン(cmd)またはPowerShellを介してコードを使用してスクリプトを起動すると、希望する動作が得られます。 しかし、同じスクリプトをEclipse / PyDevから "Python run"で起動すると、出力は常にメインのEclipse / PyDevコンソールに出力されますが、サブプロセスの2番目のコンソールは空のままで非アクティブのままです。 とはいえ、これは別のシステム/環境の専門分野であり、別の問題であると思います。

from sys import argv, stdin, stdout
from threading import Thread
from cmd import Cmd
from time import sleep
from datetime import datetime
from subprocess import Popen, PIPE, CREATE_NEW_CONSOLE

INTRO = 'command line'
PROMPT = '> '


class CommandLine(Cmd):
    """Custom console"""

    def __init__(self, subprocess, intro=INTRO, prompt=PROMPT):
        Cmd.__init__(self)
        self.subprocess = subprocess
        self.intro = intro
        self.prompt = prompt
        self.doc_header = intro
        self.running = False

    def do_date(self, args):
        """Prints the current date and time."""
        print(datetime.now())
        sleep(1)

    def do_exit(self, args):
        """Exits this command line application."""
        print("Exit by user command.")
        if self.subprocess is not None:
            try:
                self.subprocess.terminate()
            except:
                self.subprocess.kill()
        exit()


class Console():

    def __init__(self):
        if '-r' not in argv:
            self.p = Popen(
                ['python.exe', __file__, '-r'],
                stdin=PIPE,
                creationflags=CREATE_NEW_CONSOLE
            )
        else:
            while True:
                data = stdin.read(1)
                if not data:
                    #                     break
                    sleep(1)
                    continue
                stdout.write(data)

    def write(self, data):
        self.p.stdin.write(data.encode('utf8'))
        self.p.stdin.flush()

    def getSubprocess(self):
        if self.p:
            return self.p
        else:
            return None


class Feeder (Thread):

    def __init__(self, console):
        self.console = console
        Thread.__init__(self)

    def run(self):
        feeding(self.console)


def feeding(console):
    for i in range(0, 100):
        console.write('test %i\n' % i)
        sleep(1)


if __name__ == '__main__':
    p = Console()
    if '-r' not in argv:
        thread = Feeder(p)
        thread.setDaemon(True)
        thread.start()

        cl = CommandLine(subprocess=p.getSubprocess())
        cl.use_rawinput = False
        cl.prompt = PROMPT
        cl.cmdloop('\nCommand line is waiting for user input (e.g. help).')

編集-3:名誉ある言及

上記の質問のテキストでは、別の回避策としてWindowsコンソールAPIに直接アクセスするためのctypesライブラリの使用について説明しました(「編集-1:その他の考え」の下)。 あるいは、たった1つのコンソールを使って、この問題全体に対する核の選択肢として入力プロンプトが常に一番下にあるようにします。 (「編集-2:さらに考える」の下)

ctypesライブラリを使うために、私は次のWindowsのコンソールフォントの変更への答えに自分自身を向けていたでしょう。 コンソールを1つだけ使用する場合は、 コンソール入力行を出力より下保つために次の回答を試してください 。 私はこれらの答えの両方がこの問題に関して潜在的なメリットを提供するかもしれないと思いますし、多分彼らはこの記事に遭遇するどのように他の人に役立ちます。 また、私は私が時間を見つけたら私は彼らがどういうわけかうまくいけば試してみます。





real-time