如何让`setup.py test`在Windows上与`multiprocessing`一起工作?

Posted

技术标签:

【中文标题】如何让`setup.py test`在Windows上与`multiprocessing`一起工作?【英文标题】:How to get `setup.py test` working with `multiprocessing` on Windows? 【发布时间】:2018-09-24 15:39:13 【问题描述】:

问题

我正在尝试让setup.py test 使用在 Windows 上使用多处理的包。我有以下情况:

一个常规的python包文件夹结构 有一个包 一个测试文件夹和 setup.py

跑步

python -m unittest

从顶层目录执行我的测试,没有任何抱怨。但是,调用时

python setup.py test

我收到了众所周知的freeze_support 问题(请参阅此处关于 SO 的几个问题):

RuntimeError: 
    An attempt has been made to start a new process before the
    current process has finished its bootstrapping phase.
    This probably means that you are not using fork to start your
    child processes and you have forgotten to use the proper idiom
    in the main module:
        if __name__ == '__main__':
            freeze_support()
            ...
    The "freeze_support()" line can be omitted if the program
    is not going to be frozen to produce an executable.

但是,以下 where to put freeze_support() in a Python script? 实际上,我的所有文件中都有所描述的保护措施,可能太多了。

问题 1:我必须在我的项目中进行哪些更改才能使 setup.py test 使用我的多处理使用函数?

问题 2: 我放置的 freeze_support() 中的哪一个是真正需要的?据我所知,这通常只在编译冻结的可执行文件时才需要。

MWE

文件夹结构

project
│   setup.py
│
└─── my_package
│    │   __init__.py
│    │   my_package.py
│   
└─── test
│    │   __init__.py
│    │   test_it.py

__init__.py 文件为空,其他为:

my_package.py

import multiprocessing

def say_hi(arg):
    print('hi')

def main():
    pool = multiprocessing.Pool(1)
    pool.map(say_hi, range(1))

if __name__ == '__main__':
    multiprocessing.freeze_support()
    main()

test_it.py

import multiprocessing
import unittest

from my_package import my_package

class TestIt(unittest.TestCase):
    def test_say_hi(self):
        my_package.main()

if __name__ == '__main__':
    multiprocessing.freeze_support()

setup.py

#!/usr/bin/env python

import setuptools
import multiprocessing

setuptools.setup(
    name = "my_package"
)

if __name__ == '__main__':
    multiprocessing.freeze_support()

【问题讨论】:

【参考方案1】:

我遇到了类似的问题。 我发现当通过 setup.py 执行测试时,多处理创建的新进程调用 setuptools.setup 方法,这会影响某些事情。 (我不清楚根本原因……)

就我而言,这个 setup.py 解决了这个问题。

#!/usr/bin/env python

import setuptools

if __name__ == '__main__':
    setuptools.setup(
        name = "my_package",
        ....,
    )

【讨论】:

【参考方案2】:

如前所述,我认为该程序运行如果 setuptools.setup 仅在 __name__ == ‘main’ 成立时执行。

根本原因

当您使用多处理生成进程时,会导入模块,将 __name__ 属性设置为“__mp_main__”,(而不是 __main__)。

这是 multiprocessing.spawn.py

_fixup_main_from_path 的代码 sn-p
# If the parent process has sent a path through rather than a module
# name we assume it is an executable script that may contain
# non-main code that needs to be executed
old_main_modules.append(current_main)
main_module = types.ModuleType("__mp_main__")
main_content = runpy.run_path(main_path,
                              run_name="__mp_main__")
main_module.__dict__.update(main_content)
sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module

因此,如果setuptools.setup在模块顶层执行,就会发生无限递归。

【讨论】:

以上是关于如何让`setup.py test`在Windows上与`multiprocessing`一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

Python setup.py:如何让 find_packages() 识别子目录中的包

让 sphinx 使用 setup.py 中的版本

python setup.py sdist 仅包括来自***模块的 .py 源

SWIG + setup.py:ImportError:动态模块未定义初始化函数(init_foo)

distutils 打包setup.py

Python - 在 setup.py 安装之前和之后导入包模块