Python Errno 9 Mac OS X 中的错误文件描述符

Python Errno 9 Bad file descriptor in Mac OS X

我有以下代码 运行ning 多次后没有任何问题 彼此在 Linux:

def test_ftp(ftpserver):
    with FTP() as f:
        f.connect("localhost", port=ftpserver.server_port)
        f.login("M1", "bachmann")
        f.cwd("/")
        f.mkd("FOO")
        f.quit()

相同的测试在 MacOS X 中只能 运行 一次,之后它就会挂起。重启机器,是我重新运行测试的唯一方法。

ftpserver是在pytest-localftpserver中定义的一个测试夹具,我把这个夹具的代码贴在这里是因为我怀疑它是错误的原因:

class MPFTPServer(multiprocessing.Process):

    def __init__(self, username, password, ftp_home, ftp_port, **kwargs):
        self._server = SimpleFTPServer(username, password, ftp_home, ftp_port)
        self.server_home = self._server.ftp_home
        self.anon_root = self._server.anon_root
        self.server_port = self._server.ftp_port

        super().__init__(**kwargs)

    def run(self):
        self._server.serve_forever()

    def join(self):
        self._server.stop()

    def stop(self):
        self._server.stop()

@pytest.fixture(scope="session", autouse=True)
def ftpserver(request):
    """The returned ``ftpsever`` provides a threaded instance of
    ``pyftpdlib.servers.FTPServer`` running on localhost.
    ...
    """

    from pytest_localftpserver.plugin import MPFTPServer
    ftp_user = os.getenv("FTP_USER", "fakeusername")
    ftp_password = os.getenv("FTP_PASS", "qweqwe")
    ftp_home =  os.getenv("FTP_HOME", "")
    ftp_port = int(os.getenv("FTP_PORT", 0))
    server = MPFTPServer(ftp_user, ftp_password, ftp_home, ftp_port)
    # This is a must in order to clear used sockets
    server.daemon = True
    server.start()
    yield server
    server.join()

你能说出为什么这段代码 "works repeatedly" 在 Linux 而不是在 MacOSX 中吗?

更新

进一步挖掘,我发现 ftp 服务器甚至不会启动,因此挂起。代码崩溃并显示以下消息:

    Process MPFTPServer-1:
    Traceback (most recent call last):
      File "/opt/pkg/lib/python3.5/multiprocessing/process.py", line 249, in _bootstrap
        self.run()
      File "/Users/w/.virtualenvs/controller_config/lib/python3.5/site-packages/pytest_localftpserver/plugin.py", line 81, in run
        self._server.serve_forever()
      File "/Users/w/.virtualenvs/controller_config/lib/python3.5/site-packages/pyftpdlib/servers.py", line 207, in serve_forever
        self.ioloop.loop(timeout, blocking)
      File "/Users/w/.virtualenvs/controller_config/lib/python3.5/site-packages/pyftpdlib/ioloop.py", line 348, in loop
        poll(soonest_timeout)
      File "/Users/w/.virtualenvs/controller_config/lib/python3.5/site-packages/pyftpdlib/ioloop.py", line 709, in poll
        timeout)
    OSError: [Errno 9] Bad file descriptor

好的,显然 bad file descriptor in Mac OS X 是已知的:

that's what happens if you create an IOLoop before a fork and then try to use it in the child process. If you're going to use fork, you have to do it before anything creates the singleton IOLoop.

所以解决方案只是在 run 方法中启动服务器实例,而不是在 __init__:

class MPFTPServer(multiprocessing.Process):

    def __init__(self, username, password, ftp_home, ftp_port, **kwargs):
        self.username = username
        self.password = password
        self.server_home = ftp_home
        self.server_port = ftp_port

        super().__init__(**kwargs)

    def run(self):
        self._server = SimpleFTPServer(self.username, self.password,
                                       self.server_home, self.server_port)
        self._server.serve_forever()