file_truncate(): provide full "large file" support on Windows, by

dropping MS's inadequate _chsize() function.  This was inspired by
SF patch 498109 ("fileobject truncate support for win32"), which I
rejected.

libstdtypes.tex:  Someone who knows should update the availability
blurb.  For example, if it's available on Linux, it would be good to
say so.

test_largefile:  Uncommented the file.truncate() tests, and reworked to
do more.  The old comment about "permission errors" in the truncation
tests under Windows was almost certainly due to that the file wasn't open
for *write* access at this point, so of course MS wouldn't let you
truncate it.  I'd be appalled if a Unixish system did.

CAUTION:  Someone should run this test on Linux (etc) too.  The
truncation part was commented out before.  Note that test_largefile isn't
run by default.
This commit is contained in:
Tim Peters 2002-03-11 00:24:00 +00:00
parent 15d529aec5
commit fb05db2cae
4 changed files with 86 additions and 39 deletions

View file

@ -1154,9 +1154,8 @@ Files have the following methods:
\begin{methoddesc}[file]{truncate}{\optional{size}} \begin{methoddesc}[file]{truncate}{\optional{size}}
Truncate the file's size. If the optional \var{size} argument Truncate the file's size. If the optional \var{size} argument
present, the file is truncated to (at most) that size. The size present, the file is truncated to (at most) that size. The size
defaults to the current position. Availability of this function defaults to the current position.
depends on the operating system version (for example, not all Availability: Windows, many \UNIX variants.
\UNIX{} versions support this operation).
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[file]{write}{str} \begin{methoddesc}[file]{write}{str}

View file

@ -128,20 +128,29 @@ expect(os.lseek(f.fileno(), size, 0), size)
expect(f.read(1), 'a') # the 'a' that was written at the end of the file above expect(f.read(1), 'a') # the 'a' that was written at the end of the file above
f.close() f.close()
if hasattr(f, 'truncate'):
# XXX add tests for truncate if it exists if test_support.verbose:
# XXX has truncate ever worked on Windows? specifically on WinNT I get: print 'try truncate'
# "IOError: [Errno 13] Permission denied" f = open(name, 'r+b')
##try: f.seek(0, 2)
## newsize = size - 10 expect(f.tell(), size+1)
## f.seek(newsize) # Cut it back via seek + truncate with no argument.
## f.truncate() newsize = size - 10
## expect(f.tell(), newsize) f.seek(newsize)
## newsize = newsize - 1 f.truncate()
## f.seek(0) expect(f.tell(), newsize)
## f.truncate(newsize) # Ensure that truncate(bigger than true size) doesn't grow the file.
## expect(f.tell(), newsize) f.truncate(size)
##except AttributeError: expect(f.tell(), newsize)
## pass # Ensure that truncate(smaller than true size) shrinks the file.
newsize -= 1
f.seek(0)
f.truncate(newsize)
expect(f.tell(), newsize)
# cut it waaaaay back
f.truncate(1)
f.seek(0)
expect(len(f.read()), 1)
f.close()
os.unlink(name) os.unlink(name)

View file

@ -73,7 +73,7 @@ C API
- Because Python's magic number scheme broke on January 1st, we decided - Because Python's magic number scheme broke on January 1st, we decided
to stop Python development. Thanks for all the fish! to stop Python development. Thanks for all the fish!
- Some of us don't like fish, so we changed Python's magic number - Some of us don't like fish, so we changed Python's magic number
scheme to a new one. See Python/import.c for details. scheme to a new one. See Python/import.c for details.
New platforms New platforms
@ -84,6 +84,10 @@ Tests
Windows Windows
- file.truncate([newsize]) now works on Windows for all newsize values.
It used to fail if newsize didn't fit in 32 bits, reflecting a
limitation of MS _chsize (which is no longer used).
- os.waitpid() is now implemented for Windows, and can be used to block - os.waitpid() is now implemented for Windows, and can be used to block
until a specified process exits. This is similar to, but not exactly until a specified process exits. This is similar to, but not exactly
the same as, os.waitpid() on POSIX systems. If you're waiting for the same as, os.waitpid() on POSIX systems. If you're waiting for

View file

@ -10,8 +10,10 @@
#ifdef MS_WIN32 #ifdef MS_WIN32
#define fileno _fileno #define fileno _fileno
/* can (almost fully) duplicate with _chsize, see file_truncate */ /* can simulate truncate with Win32 API functions; see file_truncate */
#define HAVE_FTRUNCATE #define HAVE_FTRUNCATE
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#endif #endif
#ifdef macintosh #ifdef macintosh
@ -379,6 +381,9 @@ file_truncate(PyFileObject *f, PyObject *args)
newsizeobj = NULL; newsizeobj = NULL;
if (!PyArg_ParseTuple(args, "|O:truncate", &newsizeobj)) if (!PyArg_ParseTuple(args, "|O:truncate", &newsizeobj))
return NULL; return NULL;
/* Set newsize to current postion if newsizeobj NULL, else to the
specified value. */
if (newsizeobj != NULL) { if (newsizeobj != NULL) {
#if !defined(HAVE_LARGEFILE_SUPPORT) #if !defined(HAVE_LARGEFILE_SUPPORT)
newsize = PyInt_AsLong(newsizeobj); newsize = PyInt_AsLong(newsizeobj);
@ -389,37 +394,67 @@ file_truncate(PyFileObject *f, PyObject *args)
#endif #endif
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
} else { }
/* Default to current position*/ else {
/* Default to current position. */
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
errno = 0; errno = 0;
newsize = _portable_ftell(f->f_fp); newsize = _portable_ftell(f->f_fp);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (newsize == -1) { if (newsize == -1)
PyErr_SetFromErrno(PyExc_IOError); goto onioerror;
clearerr(f->f_fp);
return NULL;
}
} }
/* Flush the file. */
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
errno = 0; errno = 0;
ret = fflush(f->f_fp); ret = fflush(f->f_fp);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (ret != 0) goto onioerror; if (ret != 0)
goto onioerror;
#ifdef MS_WIN32 #ifdef MS_WIN32
/* can use _chsize; if, however, the newsize overflows 32-bits then /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
_chsize is *not* adequate; in this case, an OverflowError is raised */ so don't even try using it. truncate() should never grow the
if (newsize > LONG_MAX) { file, but MS SetEndOfFile will grow a file, so we need to
PyErr_SetString(PyExc_OverflowError, compare the specified newsize to the actual size. Some
"the new size is too long for _chsize (it is limited to 32-bit values)"); optimization could be done here when newsizeobj is NULL. */
return NULL; {
} else { Py_off_t currentEOF; /* actual size */
Py_BEGIN_ALLOW_THREADS HANDLE hFile;
int error;
/* First move to EOF, and set currentEOF to the size. */
errno = 0; errno = 0;
ret = _chsize(fileno(f->f_fp), (long)newsize); if (_portable_fseek(f->f_fp, 0, SEEK_END) != 0)
Py_END_ALLOW_THREADS goto onioerror;
if (ret != 0) goto onioerror; errno = 0;
currentEOF = _portable_ftell(f->f_fp);
if (currentEOF == -1)
goto onioerror;
if (newsize > currentEOF)
newsize = currentEOF; /* never grow the file */
/* Move to newsize, and truncate the file there. */
if (newsize != currentEOF) {
errno = 0;
if (_portable_fseek(f->f_fp, newsize, SEEK_SET) != 0)
goto onioerror;
Py_BEGIN_ALLOW_THREADS
errno = 0;
hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp));
error = hFile == (HANDLE)-1;
if (!error) {
error = SetEndOfFile(hFile) == 0;
if (error)
errno = EACCES;
}
Py_END_ALLOW_THREADS
if (error)
goto onioerror;
}
} }
#else #else
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS