跨 POSIX 和 Windows 系统的文件处理更加一致
项目描述
温南
(动词) 斗争, 受苦, 抗争
因为在 Windows 上管理文件的一切都很糟糕。
安装
$ python -m pip install --upgrade winnan
用法
使用winnan.open()函数就像使用Python 的内置open()函数一样。它旨在成为一个替代品。
import winnan
with winnan.open("myfile") as fileobj:
pass
返回的文件对象底层的文件描述符在 POSIX 和 Windows 系统上都具有以下两个属性 :
文件描述符是不可继承的。
当文件描述符仍然打开时,可以安排删除文件。
说“计划删除”而不是“已删除”是对文件如何在 Windows 上进入“删除挂起”状态直到最后一个文件句柄关闭的迂腐。请参阅 Mercurial 的开发人员文档,作为文件处于“删除挂起”状态时对文件进行某些操作的语义的参考。
动机
毫不奇怪,管理文件的复杂性涉及处理其他进程。根据 Python 应用程序正在做什么,可能不仅需要处理 Python 应用程序产生的进程,还需要处理机器上运行的其他进程。
正如PEP-446所记录的,在生成子进程的同时打开文件可能会导致文件描述符被无意继承。
该进程无法访问该文件,因为它正被另一个进程使用。
可能是这个问题在 Windows 上最经典的表现形式。在现代版本的 Python 中,在 Windows 上打开文件描述符时,默认设置O_NOINHERIT标志。在打开的文件描述符上设置它可以防止它被子进程继承。在 POSIX 系统上打开文件描述符时,现代版本的 Python 中也会默认设置等效的O_CLOEXEC标志。
值得一提的是,由于在旧版本的 Python 中 重定向stdin、stdout或stderr时能够设置close_fds=True的限制,设置O_NOINHERIT标志不足以防止文件描述符在同时产生子进程时被泄露。考虑使用 threading.Lock实例保护对subprocess.Popen的所有调用,以避免在旧版本的 Python 中这是一个额外的问题。
在 POSIX 系统上,可以在文件仍在另一个线程或进程中打开时unlink()文件。在 Windows 上,在所有版本的 Python 中,非O_TEMPORARY文件都使用 FILE_SHARE_READ |打开。FILE_SHARE_WRITE作为他们的共享模式。省略FILE_SHARE_DELETE 可防止另一个线程或进程在文件仍处于打开状态时尝试删除它。
这个库的目的是掩盖不同平台和旧版本 Python 中的这些行为差异。
参考
已经多次尝试解决 CPython 本身中的FILE_SHARE_DELETE问题,但不幸的是它们从未成功集成。
bpo-12939:tempfile.NamedTemporaryFile在 Windows 上不是特别有用
bpo-14243:支持在 Windows 上使用FILE_SHARE_DELETE打开文件
bpo-15244 :使用本机 Windows API添加新的io.FileIO
从 Python 3.7 开始,即使在重定向stdin、 stdout或stderr时也可以设置close_fds=True。