【问题标题】:python 3.4 multiprocessing does not work with unittestpython 3.4 多处理不适用于 unittest
【发布时间】:2014-10-28 01:49:16
【问题描述】:

我有一个使用多处理的单元测试。

从 Python 3.2 升级到 Python 3.4 后,出现以下错误。 我找不到任何提示,Python 内部发生了什么变化以及我必须改变什么才能使我的代码运行。

提前致谢。

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python341_64\lib\multiprocessing\spawn.py", line 106, in spawn_main
    exitcode = _main(fd)
  File "C:\Python341_64\lib\multiprocessing\spawn.py", line 116, in _main
    self = pickle.load(from_parent)
EOFError: Ran out of input

Error
Traceback (most recent call last):
  File "D:\test_multiproc.py", line 46, in testSmallWorkflow
    p.start()
  File "C:\Python341_64\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Python341_64\lib\multiprocessing\context.py", line 212, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Python341_64\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "C:\Python341_64\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Python341_64\lib\multiprocessing\reduction.py", line 59, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot serialize '_io.TextIOWrapper' object

按照示例代码,我如何重现错误:

import shutil
import traceback
import unittest
import time
from multiprocessing import Process
import os


class MyTest(unittest.TestCase):

    #---------------------------------------------------------------------------
    def setUp(self):
        self.working_dir = os.path.join(os.environ["TEMP"], "Testing")
        os.mkdir(self.working_dir)
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    def tearDown(self):
        try:
            time.sleep(5)
            shutil.rmtree(self.working_dir, ignore_errors=True)
        except OSError as err:
            traceback.print_tb(err)
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    def info(self, title):
        print(title)
        print('module name:', __name__)
        if hasattr(os, 'getppid'):  # only available on Unix
            print('parent process:', os.getppid())
        print('process id:', os.getpid())
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    def f(self, name):
        self.info('function f')
        print('hello', name)
    #---------------------------------------------------------------------------

    #---------------------------------------------------------------------------
    def testSmallWorkflow(self):
        self.info('main line')
        p = Process(target=self.f, args=('bob',))
        p.start()
        p.join()
    #---------------------------------------------------------------------------

【问题讨论】:

    标签: python multiprocessing python-3.4 python-unittest


    【解决方案1】:

    问题是unittest.TestCase 类本身不再是可腌制的,您必须腌制它才能腌制其绑定方法之一(self.f)。一个简单的解决方法是为您需要在子进程中调用的方法创建一个单独的类:

    class Tester:
        def info(self, title=None):
            print("title {}".format(title))
            print('module name:', __name__)
            if hasattr(os, 'getppid'):  # only available on Unix
                print('parent process:', os.getppid())
            print('process id:', os.getpid())
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def f(self, name):
            self.info('function f')
            print('hello', name)
        #-------------------------------
    
    
    class MyTest(unittest.TestCase):
    
        #---------------------------------------------------------------------------
        def setUp(self):
            self.working_dir = os.path.join(os.environ["TEMP"], "Testing")
            os.mkdir(self.working_dir)
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def tearDown(self):
            try:
                time.sleep(5)
                shutil.rmtree(self.working_dir, ignore_errors=True)
            except OSError as err:
                traceback.print_tb(err)
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def testSmallWorkflow(self):
            t = Tester()
            self.info('main line')
            p = Process(target=t.f, args=('bob',))
            p.start()
            p.join()
    

    或者,您可以使用__setstate__/__getstate__TestCase 中删除不可腌制的对象。在这种情况下,它是一个名为_Outcome 的内部类。我们在 child 中不关心它,所以我们可以将它从 pickle 状态中删除:

    class MyTest(unittest.TestCase):
    
        #---------------------------------------------------------------------------
        def setUp(self):
            self.working_dir = os.path.join(os.environ["TEMP"], "Testing")
            os.mkdir(self.working_dir)
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def tearDown(self):
            try:
                time.sleep(2)
                shutil.rmtree(self.working_dir, ignore_errors=True)
            except OSError as err:
                traceback.print_tb(err)
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def info(self, title=None):
            print("title {}".format(title))
            print('module name:', __name__)
            if hasattr(os, 'getppid'):  # only available on Unix
                print('parent process:', os.getppid())
            print('process id:', os.getpid())
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def f(self, name):
            self.info('function f')
            print('hello', name)
        #---------------------------------------------------------------------------
    
        #---------------------------------------------------------------------------
        def testSmallWorkflow(self):
            t = Tester()
            self.info('main line')
            p = Process(target=self.f, args=('bob',))
            p.start()
            p.join()
    
        def __getstate__(self):
            self_dict = self.__dict__.copy()
            del self_dict['_outcome']
            return self_dict
    
        def __setstate(self, state):
            self.__dict__.update(self_dict)
    

    【讨论】:

    • 感谢您提供非常有用且快速的答案。你说过,那个 unittest.Testcase 不再是可挑选的。你有更多关于这方面的信息吗?为什么?也许一些链接,我可以在哪里阅读?
    • @knumskull 我不知道它是否记录在任何地方;我只是通过查看代码弄清楚了。问题是TestCase 在内部使用的_Outcome 对象包含result 属性,它是unittest.runner.TextTestResult 实例。该类负责将每个测试的结果写入屏幕。它包含对 _io.TextIoWrapper 对象的引用,这些对象不能被腌制。如果我这周找点时间,我可能会深入研究一下,看看 3.2 和 3.4 之间到底发生了什么变化,也许会提供一个补丁来让 TestCase 再次可腌制。
    • 感谢您的解释。这对我帮助很大。
    猜你喜欢
    • 1970-01-01
    • 2015-02-10
    • 2018-01-31
    • 2022-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-23
    • 1970-01-01
    相关资源
    最近更新 更多