mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Wrap the basic win32 file apis
Want to eliminate use of pywin32 gradually
This commit is contained in:
parent
003a22b47c
commit
0341bf40db
@ -180,6 +180,39 @@ class BuildTest(unittest.TestCase):
|
||||
if isinstance(fmt, bytes):
|
||||
fmt = fmt.decode('ascii')
|
||||
self.assertEqual(unicode_type(time.strftime(fmt.replace('%e', '%#d'), t)), x)
|
||||
tdir = winutil.temp_path()
|
||||
path = os.path.join(tdir, 'test-create-file.txt')
|
||||
h = winutil.create_file(
|
||||
path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL)
|
||||
winutil.close_handle(h)
|
||||
self.assertRaises(OSError, winutil.close_handle, h)
|
||||
winutil.delete_file(path)
|
||||
self.assertRaises(OSError, winutil.delete_file, path)
|
||||
self.assertRaises(OSError, winutil.create_file,
|
||||
os.path.join(path, 'cannot'), winutil.GENERIC_READ, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL)
|
||||
sz = 23
|
||||
data = os.urandom(sz)
|
||||
open(path, 'wb').write(data)
|
||||
h = winutil.create_file(
|
||||
path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL)
|
||||
self.assertEqual(winutil.get_file_size(h), sz)
|
||||
self.assertRaises(OSError, winutil.set_file_pointer, h, 23, 23)
|
||||
self.assertEqual(winutil.read_file(h), data)
|
||||
self.assertEqual(winutil.read_file(h), b'')
|
||||
winutil.set_file_pointer(h, 3)
|
||||
self.assertEqual(winutil.read_file(h), data[3:])
|
||||
winutil.close_handle(h)
|
||||
self.assertEqual(winutil.nlinks(path), 1)
|
||||
npath = path + '.2'
|
||||
winutil.create_hard_link(npath, path)
|
||||
self.assertEqual(open(npath, 'rb').read(), data)
|
||||
self.assertEqual(winutil.nlinks(path), 2)
|
||||
winutil.delete_file(path)
|
||||
self.assertEqual(winutil.nlinks(npath), 1)
|
||||
winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_READONLY)
|
||||
self.assertRaises(OSError, winutil.delete_file, npath)
|
||||
winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_NORMAL)
|
||||
winutil.delete_file(npath)
|
||||
|
||||
def test_sqlite(self):
|
||||
import sqlite3
|
||||
|
@ -219,34 +219,10 @@ def case_preserving_open_file(path, mode='wb', mkdir_mode=0o777):
|
||||
return ans, fpath
|
||||
|
||||
|
||||
def old_windows_get_fileid(path):
|
||||
# we dont use this anymore as the win32 implementation reads the windows
|
||||
# registry to convert file times which is slow and breaks on systems with
|
||||
# registry issues.
|
||||
import win32file
|
||||
from pywintypes import error
|
||||
if isbytestring(path):
|
||||
path = path.decode(filesystem_encoding)
|
||||
try:
|
||||
h = win32file.CreateFileW(path, 0, 0, None, win32file.OPEN_EXISTING,
|
||||
win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
try:
|
||||
data = win32file.GetFileInformationByHandle(h)
|
||||
finally:
|
||||
win32file.CloseHandle(h)
|
||||
except (error, EnvironmentError):
|
||||
return None
|
||||
return data[4], data[8], data[9]
|
||||
|
||||
|
||||
def windows_get_fileid(path):
|
||||
''' The fileid uniquely identifies actual file contents (it is the same for
|
||||
all hardlinks to a file). Similar to inode number on linux. '''
|
||||
try:
|
||||
get_file_id = plugins['winutil'][0].get_file_id
|
||||
except AttributeError:
|
||||
# running from source without updating binary
|
||||
return old_windows_get_fileid(path)
|
||||
get_file_id = plugins['winutil'][0].get_file_id
|
||||
if isbytestring(path):
|
||||
path = path.decode(filesystem_encoding)
|
||||
with suppress(OSError):
|
||||
|
@ -369,7 +369,14 @@ winutil_strftime(PyObject *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char winutil_doc[] = "Defines utility methods to interface with windows.";
|
||||
static PyObject*
|
||||
winutil_close_handle(PyObject *self, PyObject *pyhandle) {
|
||||
if (!PyLong_Check(pyhandle)) { PyErr_SetString(PyExc_TypeError, "handle must be an int"); return NULL; }
|
||||
if (!CloseHandle(PyLong_AsVoidPtr(pyhandle))) return PyErr_SetFromWindowsErr(0);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static const char winutil_doc[] = "Defines utility methods to interface with windows.";
|
||||
extern PyObject *winutil_add_to_recent_docs(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_file_association(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_friendly_name(PyObject *self, PyObject *args);
|
||||
@ -377,6 +384,14 @@ extern PyObject *winutil_notify_associations_changed(PyObject *self, PyObject *a
|
||||
extern PyObject *winutil_move_to_trash(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_manage_shortcut(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_get_file_id(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_create_file(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_delete_file(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_create_hard_link(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_nlinks(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_set_file_attributes(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_get_file_size(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_set_file_pointer(PyObject *self, PyObject *args);
|
||||
extern PyObject *winutil_read_file(PyObject *self, PyObject *args);
|
||||
|
||||
static PyMethodDef winutil_methods[] = {
|
||||
{"special_folder_path", winutil_folder_path, METH_VARARGS,
|
||||
@ -472,6 +487,42 @@ be a unicode string. Returns unicode strings."
|
||||
"get_file_id(path)\n\nGet the windows file id (volume_num, file_index_high, file_index_low)"
|
||||
},
|
||||
|
||||
{"create_file", (PyCFunction)winutil_create_file, METH_VARARGS,
|
||||
"create_file(path, desired_access, share_mode, creation_disposition, flags_and_attributes)\n\nWrapper for CreateFile"
|
||||
},
|
||||
|
||||
{"get_file_size", (PyCFunction)winutil_get_file_size, METH_O,
|
||||
"get_file_size(handle)\n\nWrapper for GetFileSizeEx"
|
||||
},
|
||||
|
||||
{"set_file_pointer", (PyCFunction)winutil_set_file_pointer, METH_VARARGS,
|
||||
"set_file_pointer(handle, pos, method=FILE_BEGIN)\n\nWrapper for SetFilePointer"
|
||||
},
|
||||
|
||||
{"read_file", (PyCFunction)winutil_read_file, METH_VARARGS,
|
||||
"set_file_pointer(handle, chunk_size=16KB)\n\nWrapper for ReadFile"
|
||||
},
|
||||
|
||||
{"close_handle", (PyCFunction)winutil_close_handle, METH_O,
|
||||
"close_handle(handle)\n\nWrapper for CloseHandle"
|
||||
},
|
||||
|
||||
{"delete_file", (PyCFunction)winutil_delete_file, METH_VARARGS,
|
||||
"delete_file(path)\n\nWrapper for DeleteFile"
|
||||
},
|
||||
|
||||
{"create_hard_link", (PyCFunction)winutil_create_hard_link, METH_VARARGS,
|
||||
"create_hard_link(path, existing_path)\n\nWrapper for CreateHardLink"
|
||||
},
|
||||
|
||||
{"nlinks", (PyCFunction)winutil_nlinks, METH_VARARGS,
|
||||
"nlinks(path)\n\nReturn the number of hardlinks"
|
||||
},
|
||||
|
||||
{"set_file_attributes", (PyCFunction)winutil_set_file_attributes, METH_VARARGS,
|
||||
"set_file_attributes(path, attrs)\n\nWrapper for SetFileAttributes"
|
||||
},
|
||||
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
@ -514,6 +565,27 @@ CALIBRE_MODINIT_FUNC PyInit_winutil(void) {
|
||||
PyModule_AddIntConstant(m, "CSIDL_PROFILE", CSIDL_PROFILE);
|
||||
PyModule_AddIntConstant(m, "CSIDL_STARTUP", CSIDL_STARTUP);
|
||||
PyModule_AddIntConstant(m, "CSIDL_COMMON_STARTUP", CSIDL_COMMON_STARTUP);
|
||||
PyModule_AddIntConstant(m, "CREATE_NEW", CREATE_NEW);
|
||||
PyModule_AddIntConstant(m, "CREATE_ALWAYS", CREATE_ALWAYS);
|
||||
PyModule_AddIntConstant(m, "OPEN_EXISTING", OPEN_EXISTING);
|
||||
PyModule_AddIntConstant(m, "OPEN_ALWAYS", OPEN_ALWAYS);
|
||||
PyModule_AddIntConstant(m, "TRUNCATE_EXISTING", TRUNCATE_EXISTING);
|
||||
PyModule_AddIntConstant(m, "FILE_SHARE_READ", FILE_SHARE_READ);
|
||||
PyModule_AddIntConstant(m, "FILE_SHARE_WRITE", FILE_SHARE_WRITE);
|
||||
PyModule_AddIntConstant(m, "FILE_SHARE_DELETE", FILE_SHARE_DELETE);
|
||||
PyModule_AddIntConstant(m, "FILE_SHARE_VALID_FLAGS", FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
|
||||
PyModule_AddIntConstant(m, "FILE_ATTRIBUTE_READONLY", FILE_ATTRIBUTE_READONLY);
|
||||
PyModule_AddIntConstant(m, "FILE_ATTRIBUTE_NORMAL", FILE_ATTRIBUTE_NORMAL);
|
||||
PyModule_AddIntConstant(m, "FILE_ATTRIBUTE_TEMPORARY", FILE_ATTRIBUTE_TEMPORARY);
|
||||
PyModule_AddIntConstant(m, "FILE_FLAG_DELETE_ON_CLOSE", FILE_FLAG_DELETE_ON_CLOSE);
|
||||
PyModule_AddIntConstant(m, "FILE_FLAG_SEQUENTIAL_SCAN", FILE_FLAG_SEQUENTIAL_SCAN);
|
||||
PyModule_AddIntConstant(m, "FILE_FLAG_RANDOM_ACCESS", FILE_FLAG_RANDOM_ACCESS);
|
||||
PyModule_AddIntConstant(m, "GENERIC_READ", GENERIC_READ);
|
||||
PyModule_AddIntConstant(m, "GENERIC_WRITE", GENERIC_WRITE);
|
||||
PyModule_AddIntConstant(m, "DELETE", DELETE);
|
||||
PyModule_AddIntConstant(m, "FILE_BEGIN", FILE_BEGIN);
|
||||
PyModule_AddIntConstant(m, "FILE_CURRENT", FILE_CURRENT);
|
||||
PyModule_AddIntConstant(m, "FILE_END", FILE_END);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
@ -131,16 +131,108 @@ py_to_wchar(PyObject *obj, wchar_raii *output) {
|
||||
return 0;
|
||||
}
|
||||
wchar_t *buf = PyUnicode_AsWideCharString(obj, NULL);
|
||||
if (!buf) { PyErr_NoMemory(); return 0; }
|
||||
output->set_ptr(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
py_to_wchar_no_none(PyObject *obj, wchar_raii *output) {
|
||||
if (!PyUnicode_Check(obj)) {
|
||||
PyErr_SetString(PyExc_TypeError, "unicode object expected");
|
||||
return 0;
|
||||
}
|
||||
wchar_t *buf = PyUnicode_AsWideCharString(obj, NULL);
|
||||
if (!buf) { PyErr_NoMemory(); return 0; }
|
||||
output->set_ptr(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_error_from_file_handle(HANDLE h) {
|
||||
int error_code = GetLastError();
|
||||
wchar_t buf[4096] = {0};
|
||||
if (GetFinalPathNameByHandleW(h, buf, 4095, FILE_NAME_OPENED)) {
|
||||
PyObject *fname = PyUnicode_FromWideChar(buf, -1);
|
||||
if (fname) {
|
||||
PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, error_code, fname);
|
||||
Py_DECREF(fname);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return PyErr_SetFromWindowsErr(error_code);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
PyObject*
|
||||
winutil_read_file(PyObject *self, PyObject *args) {
|
||||
unsigned long chunk_size = 16 * 1024;
|
||||
PyObject *handle;
|
||||
if (!PyArg_ParseTuple(args, "O!|k", &PyLong_Type, &handle, &chunk_size)) return NULL;
|
||||
PyObject *ans = PyBytes_FromStringAndSize(NULL, chunk_size);
|
||||
if (!ans) return PyErr_NoMemory();
|
||||
DWORD bytes_read;
|
||||
if (!ReadFile(PyLong_AsVoidPtr(handle), PyBytes_AS_STRING(ans), chunk_size, &bytes_read, NULL)) {
|
||||
Py_DECREF(ans);
|
||||
return set_error_from_file_handle(PyLong_AsVoidPtr(handle));
|
||||
}
|
||||
if (bytes_read < chunk_size) _PyBytes_Resize(&ans, bytes_read);
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
winutil_get_file_size(PyObject *self, PyObject *pyhandle) {
|
||||
if (!PyLong_Check(pyhandle)) { PyErr_SetString(PyExc_TypeError, "handle must be an int"); return NULL; }
|
||||
LARGE_INTEGER ans = {0};
|
||||
if (!GetFileSizeEx(PyLong_AsVoidPtr(pyhandle), &ans)) return set_error_from_file_handle(PyLong_AsVoidPtr(pyhandle));
|
||||
return PyLong_FromLongLong(ans.QuadPart);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
winutil_set_file_pointer(PyObject *self, PyObject *args) {
|
||||
PyObject *handle; unsigned long move_method = FILE_BEGIN;
|
||||
LARGE_INTEGER pos = {0};
|
||||
if (!PyArg_ParseTuple(args, "O!L|k", &PyLong_Type, &handle, &pos.QuadPart, &move_method)) return NULL;
|
||||
LARGE_INTEGER ans = {0};
|
||||
if (!SetFilePointerEx(PyLong_AsVoidPtr(handle), pos, &ans, move_method)) return set_error_from_file_handle(PyLong_AsVoidPtr(handle));
|
||||
return PyLong_FromLongLong(ans.QuadPart);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
winutil_create_file(PyObject *self, PyObject *args) {
|
||||
wchar_raii path;
|
||||
unsigned long desired_access, share_mode, creation_disposition, flags_and_attributes;
|
||||
if (!PyArg_ParseTuple(args, "O&kkkk", py_to_wchar_no_none, &path, &desired_access, &share_mode, &creation_disposition, &flags_and_attributes)) return NULL;
|
||||
HANDLE h = CreateFileW(
|
||||
path.ptr(), desired_access, share_mode, NULL, creation_disposition, flags_and_attributes, NULL
|
||||
);
|
||||
if (h == INVALID_HANDLE_VALUE) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
|
||||
return PyLong_FromVoidPtr(h);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
winutil_delete_file(PyObject *self, PyObject *args) {
|
||||
wchar_raii path;
|
||||
if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL;
|
||||
if (!DeleteFileW(path.ptr())) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
winutil_create_hard_link(PyObject *self, PyObject *args) {
|
||||
wchar_raii path, existing_path;
|
||||
if (!PyArg_ParseTuple(args, "O&O&", py_to_wchar_no_none, &path, py_to_wchar_no_none, &existing_path)) return NULL;
|
||||
if (!CreateHardLinkW(path.ptr(), existing_path.ptr(), NULL)) return PyErr_SetExcFromWindowsErrWithFilenameObjects(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0), PyTuple_GET_ITEM(args, 1));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
winutil_get_file_id(PyObject *self, PyObject *args) {
|
||||
wchar_raii path;
|
||||
if (!PyArg_ParseTuple(args, "O&", py_to_wchar, &path)) return NULL;
|
||||
if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL;
|
||||
if (path.ptr()) {
|
||||
handle_raii file_handle(CreateFileW(path.ptr(), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
|
||||
if (!file_handle) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
|
||||
@ -153,6 +245,28 @@ winutil_get_file_id(PyObject *self, PyObject *args) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
winutil_nlinks(PyObject *self, PyObject *args) {
|
||||
wchar_raii path;
|
||||
if (!PyArg_ParseTuple(args, "O&", py_to_wchar_no_none, &path)) return NULL;
|
||||
handle_raii file_handle(CreateFileW(path.ptr(), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
|
||||
if (!file_handle) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
|
||||
BY_HANDLE_FILE_INFORMATION info = {0};
|
||||
BOOL ok = GetFileInformationByHandle(file_handle.ptr(), &info);
|
||||
if (!ok) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
|
||||
unsigned long ans = info.nNumberOfLinks;
|
||||
return PyLong_FromUnsignedLong(ans);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
winutil_set_file_attributes(PyObject *self, PyObject *args) {
|
||||
wchar_raii path; unsigned long attrs;
|
||||
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));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
winutil_add_to_recent_docs(PyObject *self, PyObject *args) {
|
||||
wchar_raii path, app_id;
|
||||
|
Loading…
x
Reference in New Issue
Block a user