Remove pywin32 from filenames.py

This commit is contained in:
Kovid Goyal 2020-10-16 18:10:25 +05:30
parent 5701a6c2c5
commit e087f086bd
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 57 additions and 74 deletions

View File

@ -185,6 +185,7 @@ class BuildTest(unittest.TestCase):
self.assertRaises(OSError, winutil.delete_file, path) self.assertRaises(OSError, winutil.delete_file, path)
self.assertRaises(OSError, winutil.create_file, self.assertRaises(OSError, winutil.create_file,
os.path.join(path, 'cannot'), winutil.GENERIC_READ, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) os.path.join(path, 'cannot'), winutil.GENERIC_READ, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL)
self.assertTrue(winutil.supports_hardlinks(os.path.abspath(os.getcwd())[0] + ':\\'))
sz = 23 sz = 23
data = os.urandom(sz) data = os.urandom(sz)
open(path, 'wb').write(data) open(path, 'wb').write(data)

View File

@ -9,7 +9,7 @@ import os
import shutil import shutil
import time import time
from math import ceil from math import ceil
from contextlib import suppress from contextlib import suppress, closing
from calibre import force_unicode, isbytestring, prints, sanitize_file_name from calibre import force_unicode, isbytestring, prints, sanitize_file_name
from calibre.constants import ( from calibre.constants import (
@ -272,25 +272,19 @@ def windows_get_size(path):
''' On windows file sizes are only accurately stored in the actual file, ''' On windows file sizes are only accurately stored in the actual file,
not in the directory entry (which could be out of date). So we open the not in the directory entry (which could be out of date). So we open the
file, and get the actual size. ''' file, and get the actual size. '''
import win32file from calibre_extensions import winutil
if isbytestring(path): if isbytestring(path):
path = path.decode(filesystem_encoding) path = path.decode(filesystem_encoding)
h = win32file.CreateFileW( with closing(winutil.create_file(
path, 0, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE | win32file.FILE_SHARE_DELETE, path, 0, winutil.FILE_SHARE_READ | winutil.FILE_SHARE_WRITE | winutil.FILE_SHARE_DELETE,
None, win32file.OPEN_EXISTING, 0, None) winutil.OPEN_EXISTING, 0)
try: ) as h:
return win32file.GetFileSize(h) return winutil.get_file_size(h)
finally:
win32file.CloseHandle(h)
def windows_hardlink(src, dest): def windows_hardlink(src, dest):
import win32file, pywintypes from calibre_extensions import winutil
try: winutil.create_hard_link(dest, src)
win32file.CreateHardLink(dest, src)
except pywintypes.error as e:
msg = 'Creating hardlink from %s to %s failed: %%s' % (src, dest)
raise OSError(msg % e)
src_size = os.path.getsize(src) src_size = os.path.getsize(src)
# We open and close dest, to ensure its directory entry is updated # We open and close dest, to ensure its directory entry is updated
# see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx # see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx
@ -311,12 +305,8 @@ def windows_hardlink(src, dest):
def windows_fast_hardlink(src, dest): def windows_fast_hardlink(src, dest):
import win32file, pywintypes from calibre_extensions import winutil
try: winutil.create_hard_link(dest, src)
win32file.CreateHardLink(dest, src)
except pywintypes.error as e:
msg = 'Creating hardlink from %s to %s failed: %%s' % (src, dest)
raise OSError(msg % e)
ssz, dsz = windows_get_size(src), windows_get_size(dest) ssz, dsz = windows_get_size(src), windows_get_size(dest)
if ssz != dsz: if ssz != dsz:
msg = 'Creating hardlink from %s to %s failed: %%s' % (src, dest) msg = 'Creating hardlink from %s to %s failed: %%s' % (src, dest)
@ -324,15 +314,10 @@ def windows_fast_hardlink(src, dest):
def windows_nlinks(path): def windows_nlinks(path):
import win32file from calibre_extensions import winutil
dwFlagsAndAttributes = win32file.FILE_FLAG_BACKUP_SEMANTICS if os.path.isdir(path) else 0
if isbytestring(path): if isbytestring(path):
path = path.decode(filesystem_encoding) path = path.decode(filesystem_encoding)
handle = win32file.CreateFileW(path, win32file.GENERIC_READ, win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, dwFlagsAndAttributes, None) return winutil.nlinks(path)
try:
return win32file.GetFileInformationByHandle(handle)[7]
finally:
handle.Close()
class WindowsAtomicFolderMove(object): class WindowsAtomicFolderMove(object):
@ -346,11 +331,9 @@ class WindowsAtomicFolderMove(object):
''' '''
def __init__(self, path): def __init__(self, path):
self.handle_map = {}
import win32file, winerror
from pywintypes import error
from collections import defaultdict from collections import defaultdict
from calibre_extensions import winutil
self.handle_map = {}
if isbytestring(path): if isbytestring(path):
path = path.decode(filesystem_encoding) path = path.decode(filesystem_encoding)
@ -368,18 +351,16 @@ class WindowsAtomicFolderMove(object):
f = os.path.normcase(os.path.abspath(os.path.join(path, x))) f = os.path.normcase(os.path.abspath(os.path.join(path, x)))
if not os.path.isfile(f): if not os.path.isfile(f):
continue continue
try: with suppress(OSError):
# Ensure the file is not read-only # Ensure the file is not read-only
win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL) winutil.set_file_attributes(f, winutil.FILE_ATTRIBUTE_NORMAL)
except:
pass
try: try:
h = win32file.CreateFileW(f, win32file.GENERIC_READ, h = winutil.create_file(f, winutil.GENERIC_READ,
win32file.FILE_SHARE_DELETE, None, winutil.FILE_SHARE_DELETE,
win32file.OPEN_EXISTING, win32file.FILE_FLAG_SEQUENTIAL_SCAN, 0) winutil.OPEN_EXISTING, winutil.FILE_FLAG_SEQUENTIAL_SCAN)
except error as e: except OSError as e:
if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION: if e.winerror == winutil.ERROR_SHARING_VIOLATION:
# The file could be a hardlink to an already opened file, # The file could be a hardlink to an already opened file,
# in which case we use the same handle for both files # in which case we use the same handle for both files
fileid = name_to_fileid[x] fileid = name_to_fileid[x]
@ -395,7 +376,7 @@ class WindowsAtomicFolderMove(object):
continue continue
self.close_handles() self.close_handles()
if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION: if e.winerror == winutil.ERROR_SHARING_VIOLATION:
err = IOError(errno.EACCES, err = IOError(errno.EACCES,
_('File is open in another process')) _('File is open in another process'))
err.filename = f err.filename = f
@ -409,9 +390,9 @@ class WindowsAtomicFolderMove(object):
self.handle_map[f] = h self.handle_map[f] = h
def copy_path_to(self, path, dest): def copy_path_to(self, path, dest):
import win32file from calibre_extensions import winutil
handle = None handle = None
for p, h in iteritems(self.handle_map): for p, h in self.handle_map.items():
if samefile_windows(path, p): if samefile_windows(path, p):
handle = h handle = h
break break
@ -421,18 +402,16 @@ class WindowsAtomicFolderMove(object):
' operation was started'%path) ' operation was started'%path)
else: else:
raise ValueError('The file %r does not exist'%path) raise ValueError('The file %r does not exist'%path)
try:
with suppress(OSError):
windows_hardlink(path, dest) windows_hardlink(path, dest)
return return
except:
pass
win32file.SetFilePointer(handle, 0, win32file.FILE_BEGIN) winutil.set_file_pointer(handle, 0, winutil.FILE_BEGIN)
with lopen(dest, 'wb') as f: with lopen(dest, 'wb') as f:
sz = 1024 * 1024
while True: while True:
hr, raw = win32file.ReadFile(handle, 1024*1024) raw = winutil.read_file(handle, sz)
if hr != 0:
raise IOError(hr, 'Error while reading from %r'%path)
if not raw: if not raw:
break break
f.write(raw) f.write(raw)
@ -445,22 +424,20 @@ class WindowsAtomicFolderMove(object):
key = (p, h) key = (p, h)
break break
if key is not None: if key is not None:
import win32file key[1].close()
win32file.CloseHandle(key[1])
remove = [f for f, h in iteritems(self.handle_map) if h is key[1]] remove = [f for f, h in iteritems(self.handle_map) if h is key[1]]
for x in remove: for x in remove:
self.handle_map.pop(x) self.handle_map.pop(x)
def close_handles(self): def close_handles(self):
import win32file
for h in itervalues(self.handle_map): for h in itervalues(self.handle_map):
win32file.CloseHandle(h) h.close()
self.handle_map = {} self.handle_map = {}
def delete_originals(self): def delete_originals(self):
import win32file from calibre_extensions import winutil
for path in self.handle_map: for path in self.handle_map:
win32file.DeleteFileW(path) winutil.delete_file(path)
self.close_handles() self.close_handles()
@ -567,20 +544,12 @@ def copyfile(src, dest):
def get_hardlink_function(src, dest): def get_hardlink_function(src, dest):
if iswindows: if not iswindows:
import win32file, win32api return os.link
colon = b':' if isinstance(dest, bytes) else ':' from calibre_extensions import winutil
root = dest[0] + colon root = dest[0] + ':\\'
try: if src[0].lower() == dest[0].lower() and hasattr(winutil, 'supports_hardlinks') and winutil.supports_hardlinks(root):
is_suitable = win32file.GetDriveType(root) not in (win32file.DRIVE_REMOTE, win32file.DRIVE_CDROM) return windows_fast_hardlink
# See https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx
supports_hard_links = win32api.GetVolumeInformation(root + os.sep)[3] & 0x00400000
except Exception:
supports_hard_links = is_suitable = False
hardlink = windows_fast_hardlink if is_suitable and supports_hard_links and src[0].lower() == dest[0].lower() else None
else:
hardlink = os.link
return hardlink
def copyfile_using_links(path, dest, dest_is_dir=True, filecopyfunc=copyfile): def copyfile_using_links(path, dest, dest_is_dir=True, filecopyfunc=copyfile):

View File

@ -510,6 +510,18 @@ winutil_delete_file(PyObject *self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject*
supports_hardlinks(PyObject *self, PyObject *args) {
wchar_raii path;
if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL;
UINT dt = GetDriveType(path.ptr());
if (dt == DRIVE_REMOTE || dt == DRIVE_CDROM) Py_RETURN_FALSE;
DWORD max_component_length, flags;
if (!GetVolumeInformationW(path.ptr(), NULL, 0, NULL, &max_component_length, &flags, NULL, 0)) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
if (flags & FILE_SUPPORTS_HARD_LINKS) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static PyObject* static PyObject*
winutil_create_hard_link(PyObject *self, PyObject *args) { winutil_create_hard_link(PyObject *self, PyObject *args) {
wchar_raii path, existing_path; wchar_raii path, existing_path;
@ -550,8 +562,8 @@ winutil_nlinks(PyObject *self, PyObject *args) {
static PyObject* static PyObject*
winutil_set_file_attributes(PyObject *self, PyObject *args) { winutil_set_file_attributes(PyObject *self, PyObject *args) {
wchar_raii path; unsigned long attrs; wchar_raii path; unsigned long attrs = FILE_ATTRIBUTE_NORMAL;
if (!PyArg_ParseTuple(args, "O&k", py_to_wchar_no_none, &path, &attrs)) return NULL; if (!PyArg_ParseTuple(args, "O&|k", py_to_wchar_no_none, &path, &attrs)) return NULL;
if (!SetFileAttributes(path.ptr(), attrs)) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0)); if (!SetFileAttributes(path.ptr(), attrs)) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -1026,6 +1038,7 @@ static PyMethodDef winutil_methods[] = {
M(is_wow64_process, METH_NOARGS), M(is_wow64_process, METH_NOARGS),
M(get_dll_directory, METH_NOARGS), M(get_dll_directory, METH_NOARGS),
M(create_mutex, METH_VARARGS), M(create_mutex, METH_VARARGS),
M(supports_hardlinks, METH_VARARGS),
M(get_async_key_state, METH_VARARGS), M(get_async_key_state, METH_VARARGS),
M(create_named_pipe, METH_VARARGS), M(create_named_pipe, METH_VARARGS),
M(connect_named_pipe, METH_VARARGS), M(connect_named_pipe, METH_VARARGS),
@ -1125,7 +1138,7 @@ static PyMethodDef winutil_methods[] = {
}, },
{"read_file", (PyCFunction)winutil_read_file, METH_VARARGS, {"read_file", (PyCFunction)winutil_read_file, METH_VARARGS,
"set_file_pointer(handle, chunk_size=16KB)\n\nWrapper for ReadFile" "read_file(handle, chunk_size=16KB)\n\nWrapper for ReadFile"
}, },
{"get_disk_free_space", (PyCFunction)winutil_get_disk_free_space, METH_VARARGS, {"get_disk_free_space", (PyCFunction)winutil_get_disk_free_space, METH_VARARGS,