从子进程调用的外部 python 脚本打印 tqdm 进度条

     2023-02-23     174

关键词:

【中文标题】从子进程调用的外部 python 脚本打印 tqdm 进度条【英文标题】:Print tqdm progress bar from external python script called by subprocess 【发布时间】:2021-04-09 08:31:06 【问题描述】:

我的主要目标是通过subprocess 在另一个python 脚本(调用者脚本)中运行一个外部python 脚本(客户端脚本)。调用者脚本的控制台显示来自客户端脚本的所有输出除了 tqdm 输出 - 所以这不是subprocess 显示输出的一般问题,而是与subprocess 交互相关的特定问题tqdm

我的次要目标是我想了解它:)。非常感谢您提供如此周到的解释。

客户端脚本 (train.py) 包含多个 tqdm 调用。到目前为止,我还没有看到各种 tqdm 参数配置之间的输出有太大差异,所以让我们使用最简单的。

train.py:

...
from tqdm import tqdm

with tqdm(total = 10, ncols = 80,
          file=sys.stdout, position = 0, leave = True,
          desc='f5b: pbar.set_postfix') as pbar:
    for i in range(10):
        pbar.update(1)
        postfix = 'loss': '0:.4f'.format(1+i)
        pbar.set_postfix(**postfix)
        sleep(0.1)

调用者脚本experiment.py执行函数execute_experiment,该函数通过参数train.py调用command_list

def execute_experiment(command_list):
    tic = time.time()
    try:
        process = subprocess.Popen(
            command_list, shell=False, 
            encoding='utf-8',
            bufsize=0,
            stdin=subprocess.DEVNULL,
            universal_newlines=True,
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE
            )
        # Poll process for new output until finished
        # Source: https://***.com/q/37401654/7769076
        while process.poll() is None:
            nextline = process.stdout.readline()
            sys.stdout.write(nextline)
            sys.stdout.flush()

    except CalledProcessError as err:
        print("CalledProcessError: 0".format(err))
        sys.exit(1)

    except OSError as err:
        print("OS error: 0".format(err))
        sys.exit(1)

    except:
        print("Unexpected error:", sys.exc_info()[0])
        raise

    if (process.returncode == 0):
        toc = time.time()
        time1 = str(round(toc - tic))
        return time1
    else:
        return 1

此脚本调用上述从 train.py 截取的代码确实返回输出,但 tqdm 输出在 0 秒后停止,如下所示:

f5b: pbar.set_postfix:   0%|                             | 0/10 [00:00<?, ?it/s]
f5b: pbar.set_postfix:  10%|█▊                | 1/10 [00:00<00:00, 22310.13it/s]

脚本调用train.py原代码返回所有输出除了tqdm输出:

Training default configuration
train.py data --use-cuda ...
device: cuda
...

评论:

    shell = False:因为python脚本调用python脚本。 shell=True时,根本不调用客户端脚本 bufsize=0: 防止缓冲 train.py 调用前面带有sys.executable,以确保在本地计算机上调用相应 conda 环境的 python 解释器。

问题:

    tqdm.set_postfix 是否会阻止将进度条输出传递到上游?我知道在调用 tqdm.set_description 时会发生这种情况,例如作者:

    pbar.set_description('已处理:%d' %(1 + i))

这段代码包含它:

def train(self, dataloader, max_batches=500, verbose=True, **kwargs):
    with tqdm(total=max_batches, disable=not verbose, **kwargs) as pbar:
        for results in self.train_iter(dataloader, max_batches=max_batches):
            pbar.update(1)
            postfix = 'loss': '0:.4f'.format(results['mean_outer_loss'])

            if 'accuracies_after' in results:
                postfix['accuracy'] = '0:.4f'.format(
                    np.mean(results['accuracies_after']))
            pbar.set_postfix(**postfix)
    # for logging
    return results
    是嵌套函数调用导致进度条不显示的原因吗?

调用顺序为experiment.py > train.py > nested.py

train.py 通过以下方式调用nested.py 中的train 函数:

对于范围内的纪元(args.num_epochs):

results_metatraining = metalearner.train(meta_train_dataloader,
                  max_batches=args.num_batches,
                  verbose=args.verbose,
                  desc='Training',
                  # leave=False
                  leave=True
                  ) 

尝试了替代方案但没有成功:

    ### try2
    process = subprocess.Popen(command_list, shell=False, encoding='utf-8',
                               stdin=DEVNULL, stdout=subprocess.PIPE)
    while True:
        output = process.stdout.readline().strip()
        print('output: ' + output)
        if output == '' and process.poll() is not None:  # end of output
            break
        if output: # print output in realtime
            print(output)
    else:
        output = process.communicate()
    process.wait()


    ### try6
    process = subprocess.Popen(command_list, shell=False,
                               stdout=subprocess.PIPE, universal_newlines=True)
    for stdout_line in iter(process.stdout.readline, ""):
        yield stdout_line 
    process.stdout.close()
    return_code = process.wait()
    print('return_code' + str(return_code))
    if return_code:
        raise subprocess.CalledProcessError(return_code, command_list)


    ### try7
    with subprocess.Popen(command_list, stdout=subprocess.PIPE, 
                          bufsize=1, universal_newlines=True) as p:
        while True:
            line = p.stdout.readline()
            if not line:
                break
            print(line)    
        exit_code = p.poll()

【问题讨论】:

【参考方案1】:

我认为 readline 正在等待'\n',而 tqdm 没有创建新行,也许这会有所帮助(我没有尝试):

import io
def execute_experiment(command_list):
    tic = time.time()
    try:
        process = subprocess.Popen(
            command_list, shell=False, 
            encoding='utf-8',
            bufsize=1,
            stdin=subprocess.DEVNULL,
            universal_newlines=True,
            stdout=subprocess.PIPE, 
            stderr=subprocess.STDOUT
            )
        # Poll process for new output until finished
        # Source: https://***.com/q/37401654/7769076
        reader = io.TextIOWrapper(process.stdout, encoding='utf8')
        while process.poll() is None:
            char = reader.read(1)
            sys.stdout.write(char)
            sys.stdout.flush()

    except CalledProcessError as err:
        print("CalledProcessError: 0".format(err))
        sys.exit(1)

    except OSError as err:
        print("OS error: 0".format(err))
        sys.exit(1)

    except:
        print("Unexpected error:", sys.exc_info()[0])
        raise

    if (process.returncode == 0):
        toc = time.time()
        time1 = str(round(toc - tic))
        return time1
    else:
        return 1

【讨论】:

如何在python脚本中获取exe的输出?

...案1】:要从Python调用外部程序,请使用subprocess模块。子进程模块允许您生成新进程,连接到它 查看详情

从子进程中实时捕获标准输出

】从子进程中实时捕获标准输出【英文标题】:catchingstdoutinrealtimefromsubprocess【发布时间】:2010-12-0901:44:22【问题描述】:我想在Windows中subprocess.Popen()rsync.exe,并在Python中打印标准输出。我的代码可以工作,但在文件传输完成之... 查看详情

在 Python 中分析子进程 Popen 调用

】在Python中分析子进程Popen调用【英文标题】:ProfilingsubprocessPopencallsinPython【发布时间】:2012-06-1909:33:33【问题描述】:我有一个程序通过使用子进程中的popen调用多个外部脚本。现在我想介绍这个程序。虽然我可以使用cProfile... 查看详情

如何从子进程 python 2.7 和 Apache 读取实时输出

】如何从子进程python2.7和Apache读取实时输出【英文标题】:Howtoreadliveoutputfromsubprocesspython2.7andApache【发布时间】:2015-07-1314:20:37【问题描述】:我有一个ApacheWeb服务器,我编写了一个python脚本来运行一个命令。我正在运行的命令... 查看详情

如何从子进程中获取环境?

】如何从子进程中获取环境?【英文标题】:Howtogetenvironmentfromasubprocess?【发布时间】:2010-11-1521:58:57【问题描述】:我想通过python程序调用一个进程,但是,这个进程需要一些由另一个进程设置的特定环境变量。如何获取第一... 查看详情

通过 npm 脚本生成时从子进程向父进程发送消息

】通过npm脚本生成时从子进程向父进程发送消息【英文标题】:Sendingamessagefromchildtoparentprocesswhenspawnedvianpmscript【发布时间】:2016-09-1922:20:42【问题描述】:当我通过npm脚本生成子进程时,我失去了ipc通道。我有一个这样的子脚... 查看详情

调用外部 egrep 和 less 时子进程非常慢

】调用外部egrep和less时子进程非常慢【英文标题】:Subprocessveryslowwhencallingexternalegrepandless【发布时间】:2015-07-1422:50:31【问题描述】:我正在尝试构建一个python脚本,它允许我在egrep-v属性上动态构建并将输出通过管道传输到更... 查看详情

在python中控制用于调用外部命令的子进程数

】在python中控制用于调用外部命令的子进程数【英文标题】:Controlthenumberofsubprocessesusingtocallexternalcommandsinpython【发布时间】:2012-04-0605:07:29【问题描述】:我了解使用subprocess是调用外部命令的首选方式。但是,如果我想并行运... 查看详情

open*** 进程调用的批处理脚本 (.bat) 无法成功将参数传递给外部 .exe

】open***进程调用的批处理脚本(.bat)无法成功将参数传递给外部.exe【英文标题】:batchscript(.bat)calledbyopen***processcannotsuccessfullypassargumentstoexternal.exe【发布时间】:2020-03-2217:38:30【问题描述】:我有一个在Windows10主机上运行的open***... 查看详情

Qt 调用外部 Python 脚本

】Qt调用外部Python脚本【英文标题】:QtCallingExternalPythonScript【发布时间】:2013-02-2803:43:08【问题描述】:我正在尝试为我用Python编写的命令行工具之一编写GUI包装器。有人建议我应该使用Qt。下面是我项目的.cpp文件:#include"v_1.h... 查看详情

Python - 调用 perl 作为子进程 - 等待完成其后台进程并打印到 shell

】Python-调用perl作为子进程-等待完成其后台进程并打印到shell【英文标题】:Python-callperlassubprocess-waitforfinishitsbackgroundprocessandprinttoshell【发布时间】:2021-11-2915:45:25【问题描述】:我有一个Perl脚本,内部调用system("something&... 查看详情

如何使用从外部脚本调用的回调发送信号?

】如何使用从外部脚本调用的回调发送信号?【英文标题】:Howtosendsignalusingcallbackcalledfromexternalscript?【发布时间】:2019-03-0519:48:16【问题描述】:简介我正在尝试根据嵌入式python脚本中的计算状态更新QTGUI元素。我能够从python... 查看详情

如何运行从子文件夹导入的 Python 3 脚本?

】如何运行从子文件夹导入的Python3脚本?【英文标题】:HowcanIrunaPython3scriptwithimportsfromasubfolder?【发布时间】:2018-08-1205:55:40【问题描述】:无论我尝试什么,自从我切换到Python3我只能从项目的根文件夹运行导入脚本,但不能... 查看详情

无法从子进程访问内存映射(Python 3.8)

】无法从子进程访问内存映射(Python3.8)【英文标题】:Can\'taccessmemorymapfromchildprocess(Python3.8)【发布时间】:2020-09-0400:17:42【问题描述】:我正在编写一个程序,该程序使用Python的multiprocessing模块来加速CPU密集型任务,并且我希... 查看详情

在 Linux 上使用 Python 从子进程中杀死父进程

】在Linux上使用Python从子进程中杀死父进程【英文标题】:KillingparentprocessfromachildprocesswithPythononLinux【发布时间】:2018-12-2016:42:26【问题描述】:在我的(非常简化的)场景中,在python2.7中,我有2个进程:父进程,它执行一些任... 查看详情

-调用外部python编辑器

在PowerBI调用Python过程中,有内置的Python编辑界面,但是在实际使用过程中,在PowerBI的Python界面也会有不方便的时候,而PowerBI预留了调用外部Python编辑器来实现Python代码的编写,我们接下来就给大家分享如何进行外部Python编辑器... 查看详情

-调用外部python编辑器

在PowerBI调用Python过程中,有内置的Python编辑界面,但是在实际使用过程中,在PowerBI的Python界面也会有不方便的时候,而PowerBI预留了调用外部Python编辑器来实现Python代码的编写,我们接下来就给大家分享如何进行外部Python编辑器... 查看详情

python multiprocessing:如何从子进程修改在主进程中创建的字典?

】pythonmultiprocessing:如何从子进程修改在主进程中创建的字典?【英文标题】:pythonmultiprocessing:Howtomodifyadictionarycreatedinthemainprocessfromasubprocess?【发布时间】:2020-12-2411:25:24【问题描述】:此问题与:multiprocessing:HowdoIshareadictamong... 查看详情