[Python-checkins] r45898 - in python/trunk: Lib/test/test_os.py Lib/test/test_shutil.py Misc/NEWS Modules/posixmodule.c

martin.v.loewis python-checkins at python.org
Thu May 4 12:08:44 CEST 2006


Author: martin.v.loewis
Date: Thu May  4 12:08:42 2006
New Revision: 45898

Modified:
   python/trunk/Lib/test/test_os.py
   python/trunk/Lib/test/test_shutil.py
   python/trunk/Misc/NEWS
   python/trunk/Modules/posixmodule.c
Log:
Implement os.{chdir,rename,rmdir,remove} using Win32 directly.

Modified: python/trunk/Lib/test/test_os.py
==============================================================================
--- python/trunk/Lib/test/test_os.py	(original)
+++ python/trunk/Lib/test/test_os.py	Thu May  4 12:08:42 2006
@@ -5,6 +5,7 @@
 import os
 import unittest
 import warnings
+import sys
 from test import test_support
 
 warnings.filterwarnings("ignore", "tempnam", RuntimeWarning, __name__)
@@ -364,6 +365,20 @@
         except NotImplementedError:
             pass
 
+class Win32ErrorTests(unittest.TestCase):
+    def test_rename(self):
+        self.assertRaises(WindowsError, os.rename, test_support.TESTFN, test_support.TESTFN+".bak")
+
+    def test_remove(self):
+        self.assertRaises(WindowsError, os.remove, test_support.TESTFN)
+
+    def test_chdir(self):
+        self.assertRaises(WindowsError, os.chdir, test_support.TESTFN)
+
+if sys.platform != 'win32':
+    class Win32ErrorTests(unittest.TestCase):
+        pass
+
 def test_main():
     test_support.run_unittest(
         TemporaryFileTests,
@@ -372,7 +387,8 @@
         WalkTests,
         MakedirTests,
         DevNullTests,
-        URandomTests
+        URandomTests,
+        Win32ErrorTests
     )
 
 if __name__ == "__main__":

Modified: python/trunk/Lib/test/test_shutil.py
==============================================================================
--- python/trunk/Lib/test/test_shutil.py	(original)
+++ python/trunk/Lib/test/test_shutil.py	Thu May  4 12:08:42 2006
@@ -48,12 +48,12 @@
         if self.errorState == 0:
             self.assertEqual(func, os.remove)
             self.assertEqual(arg, self.childpath)
-            self.assertEqual(exc[0], OSError)
+            self.failUnless(issubclass(exc[0], OSError))
             self.errorState = 1
         else:
             self.assertEqual(func, os.rmdir)
             self.assertEqual(arg, TESTFN)
-            self.assertEqual(exc[0], OSError)
+            self.failUnless(issubclass(exc[0], OSError))
             self.errorState = 2
 
     def test_rmtree_dont_delete_file(self):

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Thu May  4 12:08:42 2006
@@ -67,6 +67,9 @@
 Extension Modules
 -----------------
 
+- Use Win32 API to implement os.{chdir,rename,rmdir,remove}. As a result,
+  these functions now raise WindowsError instead of OSError.
+
 - Calling Tk_Init twice is refused if the first call failed as that
   may deadlock.
 

Modified: python/trunk/Modules/posixmodule.c
==============================================================================
--- python/trunk/Modules/posixmodule.c	(original)
+++ python/trunk/Modules/posixmodule.c	Thu May  4 12:08:42 2006
@@ -458,21 +458,29 @@
 
 static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj)
 {
-	/* XXX Perhaps we should make this API an alias of
-	   PyObject_Unicode() instead ?! */
-	if (PyUnicode_CheckExact(obj)) {
-		Py_INCREF(obj);
-		return obj;
-	}
-	if (PyUnicode_Check(obj)) {
+}
+
+/* Function suitable for O& conversion */
+static int
+convert_to_unicode(PyObject *arg, void* _param)
+{
+	PyObject **param = (PyObject**)_param;
+	if (PyUnicode_CheckExact(arg)) {
+		Py_INCREF(arg);
+		*param = arg;
+	} 
+	else if (PyUnicode_Check(arg)) {
 		/* For a Unicode subtype that's not a Unicode object,
 		   return a true Unicode object with the same data. */
-	return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj),
-	                             PyUnicode_GET_SIZE(obj));
+		*param = PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(arg),
+					       PyUnicode_GET_SIZE(arg));
+		return *param != NULL;
 	}
-	return PyUnicode_FromEncodedObject(obj,
-	                                   Py_FileSystemDefaultEncoding,
-	                                   "strict");
+	else
+		*param = PyUnicode_FromEncodedObject(arg,
+				                     Py_FileSystemDefaultEncoding,
+					             "strict");
+	return (*param) != NULL;
 }
 
 #endif /* Py_WIN_WIDE_FILENAMES */
@@ -589,35 +597,10 @@
 #endif
 
 static PyObject *
-posix_1str(PyObject *args, char *format, int (*func)(const char*),
-	   char *wformat, int (*wfunc)(const Py_UNICODE*))
+posix_1str(PyObject *args, char *format, int (*func)(const char*))
 {
 	char *path1 = NULL;
 	int res;
-#ifdef Py_WIN_WIDE_FILENAMES
-	if (unicode_file_names()) {
-		PyUnicodeObject *po;
-		if (PyArg_ParseTuple(args, wformat, &po)) {
-			Py_BEGIN_ALLOW_THREADS
-			/*  PyUnicode_AS_UNICODE OK without thread
-			    lock as it is a simple dereference. */
-			res = (*wfunc)(PyUnicode_AS_UNICODE(po));
-			Py_END_ALLOW_THREADS
-			if (res < 0)
-				return posix_error_with_unicode_filename(PyUnicode_AS_UNICODE(po));
-			Py_INCREF(Py_None);
-			return Py_None;
-		}
-		/* Drop the argument parsing error as narrow
-		   strings are also valid. */
-		PyErr_Clear();
-	}
-#else
-	/* Platforms that don't support Unicode filenames
-	   shouldn't be passing these extra params */
-	assert(wformat==NULL && wfunc == NULL);
-#endif
-
 	if (!PyArg_ParseTuple(args, format,
 	                      Py_FileSystemDefaultEncoding, &path1))
 		return NULL;
@@ -634,52 +617,10 @@
 static PyObject *
 posix_2str(PyObject *args,
 	   char *format,
-	   int (*func)(const char *, const char *),
-	   char *wformat,
-	   int (*wfunc)(const Py_UNICODE *, const Py_UNICODE *))
+	   int (*func)(const char *, const char *))
 {
 	char *path1 = NULL, *path2 = NULL;
 	int res;
-#ifdef Py_WIN_WIDE_FILENAMES
-	if (unicode_file_names()) {
-		PyObject *po1;
-		PyObject *po2;
-		if (PyArg_ParseTuple(args, wformat, &po1, &po2)) {
-			if (PyUnicode_Check(po1) || PyUnicode_Check(po2)) {
-				PyObject *wpath1;
-				PyObject *wpath2;
-				wpath1 = _PyUnicode_FromFileSystemEncodedObject(po1);
-				wpath2 = _PyUnicode_FromFileSystemEncodedObject(po2);
-				if (!wpath1 || !wpath2) {
-					Py_XDECREF(wpath1);
-					Py_XDECREF(wpath2);
-					return NULL;
-				}
-				Py_BEGIN_ALLOW_THREADS
-				/* PyUnicode_AS_UNICODE OK without thread
-				   lock as it is a simple dereference.  */
-				res = (*wfunc)(PyUnicode_AS_UNICODE(wpath1),
-					       PyUnicode_AS_UNICODE(wpath2));
-				Py_END_ALLOW_THREADS
-				Py_XDECREF(wpath1);
-				Py_XDECREF(wpath2);
-				if (res != 0)
-					return posix_error();
-				Py_INCREF(Py_None);
-				return Py_None;
-			}
-			/* Else flow through as neither is Unicode. */
-		}
-		/* Drop the argument parsing error as narrow
-		   strings are also valid. */
-		PyErr_Clear();
-	}
-#else
-	/* Platforms that don't support Unicode filenames
-	   shouldn't be passing these extra params */
-	assert(wformat==NULL && wfunc == NULL);
-#endif
-
 	if (!PyArg_ParseTuple(args, format,
 	                      Py_FileSystemDefaultEncoding, &path1,
 	                      Py_FileSystemDefaultEncoding, &path2))
@@ -696,6 +637,101 @@
 	return Py_None;
 }
 
+#ifdef Py_WIN_WIDE_FILENAMES
+static PyObject*
+win32_1str(PyObject* args, char* func, 
+	   char* format, BOOL (__stdcall *funcA)(LPCSTR), 
+	   char* wformat, BOOL (__stdcall *funcW)(LPWSTR))
+{
+	PyObject *uni;
+	char *ansi;
+	BOOL result;
+	if (unicode_file_names()) {
+		if (!PyArg_ParseTuple(args, wformat, &uni))
+			PyErr_Clear();
+		else {
+			Py_BEGIN_ALLOW_THREADS
+			result = funcW(PyUnicode_AsUnicode(uni));
+			Py_END_ALLOW_THREADS
+			if (!result)
+				return win32_error_unicode(func, PyUnicode_AsUnicode(uni));
+			Py_INCREF(Py_None);
+			return Py_None;
+		}
+	}
+	if (!PyArg_ParseTuple(args, format, &ansi))
+		return NULL;
+	Py_BEGIN_ALLOW_THREADS
+	result = funcA(ansi);
+	Py_END_ALLOW_THREADS
+	if (!result)
+		return win32_error(func, ansi);
+	Py_INCREF(Py_None);
+	return Py_None;
+
+}
+
+/* This is a reimplementation of the C library's chdir function,
+   but one that produces Win32 errors instead of DOS error codes.
+   chdir is essentially a wrapper around SetCurrentDirectory; however,
+   it also needs to set "magic" environment variables indicating
+   the per-drive current directory, which are of the form =<drive>: */
+BOOL __stdcall
+win32_chdir(LPCSTR path)
+{
+	char new_path[MAX_PATH+1];
+	int result;
+	char env[4] = "=x:";
+
+	if(!SetCurrentDirectoryA(path))
+		return FALSE;
+	result = GetCurrentDirectoryA(MAX_PATH+1, new_path);
+	if (!result)
+		return FALSE;
+	/* In the ANSI API, there should not be any paths longer
+	   than MAX_PATH. */
+	assert(result <= MAX_PATH+1);
+	if (strncmp(new_path, "\\\\", 2) == 0 ||
+	    strncmp(new_path, "//", 2) == 0)
+	    /* UNC path, nothing to do. */
+	    return TRUE;
+	env[1] = new_path[0];
+	return SetEnvironmentVariableA(env, new_path);
+}
+
+/* The Unicode version differs from the ANSI version
+   since the current directory might exceed MAX_PATH characters */
+BOOL __stdcall
+win32_wchdir(LPCWSTR path)
+{
+	wchar_t _new_path[MAX_PATH+1], *new_path = _new_path;
+	int result;
+	wchar_t env[4] = L"=x:";
+
+	if(!SetCurrentDirectoryW(path))
+		return FALSE;
+	result = GetCurrentDirectoryW(MAX_PATH+1, new_path);
+	if (!result)
+		return FALSE;
+	if (result > MAX_PATH+1) {
+		new_path = malloc(result);
+		if (!new_path) {
+			SetLastError(ERROR_OUTOFMEMORY);
+			return FALSE;
+		}
+	}
+	if (wcsncmp(new_path, L"\\\\", 2) == 0 ||
+	    wcsncmp(new_path, L"//", 2) == 0)
+	    /* UNC path, nothing to do. */
+	    return TRUE;
+	env[1] = new_path[0];
+	result = SetEnvironmentVariableW(env, new_path);
+	if (new_path != _new_path)
+		free(new_path);
+	return result;
+}
+#endif
+
 #ifdef MS_WINDOWS
 /* The CRT of Windows has a number of flaws wrt. its stat() implementation:
    - time stamps are restricted to second resolution
@@ -1410,14 +1446,13 @@
 posix_chdir(PyObject *self, PyObject *args)
 {
 #ifdef MS_WINDOWS
-	return posix_1str(args, "et:chdir", chdir, "U:chdir", _wchdir);
+	return win32_1str(args, "chdir", "s:chdir", win32_chdir, "U:chdir", win32_wchdir);
 #elif defined(PYOS_OS2) && defined(PYCC_GCC)
-	return posix_1str(args, "et:chdir", _chdir2, NULL, NULL);
+	return posix_1str(args, "et:chdir", _chdir2);
 #elif defined(__VMS)
-	return posix_1str(args, "et:chdir", (int (*)(const char *))chdir,
-			  NULL, NULL);
+	return posix_1str(args, "et:chdir", (int (*)(const char *))chdir);
 #else
-	return posix_1str(args, "et:chdir", chdir, NULL, NULL);
+	return posix_1str(args, "et:chdir", chdir);
 #endif
 }
 
@@ -1485,7 +1520,7 @@
 static PyObject *
 posix_chroot(PyObject *self, PyObject *args)
 {
-	return posix_1str(args, "et:chroot", chroot, NULL, NULL);
+	return posix_1str(args, "et:chroot", chroot);
 }
 #endif
 
@@ -2071,7 +2106,6 @@
 }
 #endif /* HAVE_NICE */
 
-
 PyDoc_STRVAR(posix_rename__doc__,
 "rename(old, new)\n\n\
 Rename a file or directory.");
@@ -2080,7 +2114,36 @@
 posix_rename(PyObject *self, PyObject *args)
 {
 #ifdef MS_WINDOWS
-	return posix_2str(args, "etet:rename", rename, "OO:rename", _wrename);
+	PyObject *o1, *o2;
+	char *p1, *p2;
+	BOOL result;
+	if (unicode_file_names()) {
+	    if (!PyArg_ParseTuple(args, "O&O&:rename", 
+		convert_to_unicode, &o1,
+		convert_to_unicode, &o2))
+		    PyErr_Clear();
+	    else {
+		    Py_BEGIN_ALLOW_THREADS
+		    result = MoveFileW(PyUnicode_AsUnicode(o1),
+				       PyUnicode_AsUnicode(o2));
+		    Py_END_ALLOW_THREADS
+		    Py_DECREF(o1);
+		    Py_DECREF(o2);
+		    if (!result)
+			    return win32_error("rename", NULL);
+		    Py_INCREF(Py_None);
+		    return Py_None;
+	    }
+	}
+	if (!PyArg_ParseTuple(args, "ss:rename", &p1, &p2))
+		return NULL;
+	Py_BEGIN_ALLOW_THREADS
+	result = MoveFileA(p1, p2);
+	Py_END_ALLOW_THREADS
+	if (!result)
+		return win32_error("rename", NULL);
+	Py_INCREF(Py_None);
+	return Py_None;
 #else
 	return posix_2str(args, "etet:rename", rename, NULL, NULL);
 #endif
@@ -2095,9 +2158,9 @@
 posix_rmdir(PyObject *self, PyObject *args)
 {
 #ifdef MS_WINDOWS
-	return posix_1str(args, "et:rmdir", rmdir, "U:rmdir", _wrmdir);
+	return win32_1str(args, "rmdir", "s:rmdir", RemoveDirectoryA, "U:rmdir", RemoveDirectoryW);
 #else
-	return posix_1str(args, "et:rmdir", rmdir, NULL, NULL);
+	return posix_1str(args, "et:rmdir", rmdir);
 #endif
 }
 
@@ -2166,9 +2229,9 @@
 posix_unlink(PyObject *self, PyObject *args)
 {
 #ifdef MS_WINDOWS
-	return posix_1str(args, "et:remove", unlink, "U:remove", _wunlink);
+	return win32_1str(args, "remove", "s:remove", DeleteFileA, "U:remove", DeleteFileW);
 #else
-	return posix_1str(args, "et:remove", unlink, NULL, NULL);
+	return posix_1str(args, "et:remove", unlink);
 #endif
 }
 


More information about the Python-checkins mailing list