More consistent file handling across POSIX and Windows systems
Project description
winnan
(verb) to struggle, suffer, contend
Because everything about managing files on Windows is terrible.
Installation
$ python -m pip install --upgrade winnan
Usage
Use the winnan.open() function exactly like using Python’s built-in open() function. It aims to be a drop-in replacement.
import winnan
with winnan.open("myfile") as fileobj:
pass
The file descriptor underlying the returned file object has the following two properties on both POSIX and Windows systems:
The file descriptor is non-inheritable.
The file can be scheduled for deletion while 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. See Mercurial’s developer documentation as a reference for the semantics of certain operations on the file while it is in a “delete pending” state.
Motivation
Unsurprisingly, 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 by PEP-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, the O_NOINHERIT flag 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 equivalent O_CLOEXEC flag 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 set close_fds=True when redirecting stdin, stdout, or stderr in older versions of Python, setting the O_NOINHERIT flag isn’t sufficient for preventing files descriptors from being leaked when spawning subprocesses concurrently. Consider guarding all calls to subprocess.Popen with a threading.Lock instance to avoid this as an additional issue in older versions of Python.
On POSIX systems, it is possible to unlink() a file while it is still open in another thread or process. On Windows, in all versions of Python, non-O_TEMPORARY files are opened with FILE_SHARE_READ | FILE_SHARE_WRITE as their sharing mode. Omitting FILE_SHARE_DELETE prevents 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.
References
There have been multiple attempts to address the FILE_SHARE_DELETE issue within CPython itself but they unfortunately never succeeded in being integrated.
bpo-12939: tempfile.NamedTemporaryFile not particularly useful on Windows
bpo-14243: Support for opening files with FILE_SHARE_DELETE on Windows
bpo-15244: Add new io.FileIO using the native Windows API
Starting in Python 3.7, close_fds=True is able to be set even when redirecting stdin, stdout, or stderr.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file winnan-0.1.0.tar.gz
.
File metadata
- Download URL: winnan-0.1.0.tar.gz
- Upload date:
- Size: 50.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.14.2 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d89ae0feb03a62ecca4dfef295daf801668cea3eb5034e296a2f2bb827c0e822 |
|
MD5 | 3d6c6146e793989bfb030e3bb64cd8c1 |
|
BLAKE2b-256 | 8d7fbe635e9992297b636c7ccf3dd60ab60f95a4db499d8e0d77e182531a2c11 |