Package: winnan
winnan(verb) to struggle, suffer, contendhttps://en.wiktionary.org/wiki/winnanBecause everything about managing files on Windows is terrible.Installation$python-mpipinstall--upgradewinnanUsageUse thewinnan.open()function exactly like usingPython’s built-inopen()function. It aims
to be a drop-in replacement.importwinnanwithwinnan.open("myfile")asfileobj:passThe file descriptor underlying the returnedfileobject has the following two propertieson both POSIX and Windows systems:The file descriptor is non-inheritable.The file can bescheduled for deletionwhile the file descriptor is still open.Saying “scheduled for deletion” rather than “deleted” is to be pedantic about how the file is put
into a “delete pending” state on Windows until the last file handle is closed. SeeMercurial’s developer documentationas a reference for the semantics of certain operations on
the file while it is in a “delete pending” state.MotivationUnsurprisingly, the complications around managing files involve dealing with other processes.
Depending on what the Python application is doing, there may be a need to deal with not only
processes spawned by the Python application but also other processes running on the machine.As documented byPEP-446, opening a file concurrently with spawning a subprocess may lead to the
file descriptor being inherited unintentionally.The process cannot access the file because it is being used by another process.is perhaps the most classic representation of this issue on Windows. In modern versions of
Python, theO_NOINHERITflag is set by default when opening file descriptors on Windows.
Setting it on the opened file descriptor prevents it from being inherited by a child process. The
equivalentO_CLOEXECflag is also set by default in modern versions of Python when opening
file descriptors on POSIX systems.It is worth mentioning that due to limitations with being able to setclose_fds=Truewhen
redirectingstdin,stdout, orstderrin older versions of Python, setting theO_NOINHERITflag isn’t sufficient for preventing files descriptors from being leaked when
spawning subprocesses concurrently. Consider guarding all calls tosubprocess.Popenwith athreading.Lockinstance to avoid this as an additional issue in older versions of Python.On POSIX systems, it is possible tounlink()a file while it is still open in another thread
or process. On Windows, in all versions of Python, non-O_TEMPORARYfiles are opened withFILE_SHARE_READ | FILE_SHARE_WRITEas their sharing mode. OmittingFILE_SHARE_DELETEprevents another thread or process from attempting to delete a file while it is still open.The purpose of this library is to mask these behavioral differences across different platforms and
in older versions of Python.ReferencesThere have been multiple attempts to address theFILE_SHARE_DELETEissue within CPython itself
but they unfortunately never succeeded in being integrated.bpo-12939:tempfile.NamedTemporaryFilenot particularly useful on Windowsbpo-14243: Support for opening files withFILE_SHARE_DELETEon Windowsbpo-15244: Add newio.FileIOusing the native Windows APIStarting in Python 3.7,close_fds=Trueis able to be set even when redirectingstdin,stdout, orstderr.bpo-19575:subprocess: on Windows, unwanted file handles are inherited by child processes
in a multi-threaded applicationbpo-19764:subprocess: usePROC_THREAD_ATTRIBUTE_HANDLE_LISTwithSTARTUPINFOEXon
Windows Vistabpo-33369: RemovingPopenlog files in threads is racy on Windows