Change Windows file.truncate() to (a) restore the original file position,

and (b) stop trying to prevent file growth.

Beef up the file.truncate() docs.

Change test_largefile.py to stop assuming that f.truncate() moves the
file pointer to the truncation point, and to verify instead that it leaves
the file position alone.  Remove the test for what happens when a
specified size exceeds the original file size (it's ill-defined, according
to the Single Unix Spec).
This commit is contained in:
Tim Peters 2002-03-12 03:04:44 +00:00
parent 9d142adfce
commit 8f01b680c8
3 changed files with 75 additions and 51 deletions

View File

@ -1152,9 +1152,14 @@ Files have the following methods:
\end{methoddesc} \end{methoddesc}
\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 is
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. defaults to the current position. The current file position is
not changed. Note that if a specified size exceeds the file's
current size, the result is platform-dependent: possibilities
include that file may remain unchanged, increase to the specified
size as if zero-filled, or increase to the specified size with
undefined new content.
Availability: Windows, many \UNIX variants. Availability: Windows, many \UNIX variants.
\end{methoddesc} \end{methoddesc}

View File

@ -133,24 +133,30 @@ if hasattr(f, 'truncate'):
print 'try truncate' print 'try truncate'
f = open(name, 'r+b') f = open(name, 'r+b')
f.seek(0, 2) f.seek(0, 2)
expect(f.tell(), size+1) expect(f.tell(), size+1) # else we've lost track of the true size
# Cut it back via seek + truncate with no argument. # Cut it back via seek + truncate with no argument.
newsize = size - 10 newsize = size - 10
f.seek(newsize) f.seek(newsize)
f.truncate() f.truncate()
expect(f.tell(), newsize) expect(f.tell(), newsize) # else pointer moved
# Ensure that truncate(bigger than true size) doesn't grow the file. f.seek(0, 2)
f.truncate(size) expect(f.tell(), newsize) # else wasn't truncated
expect(f.tell(), newsize)
# Ensure that truncate(smaller than true size) shrinks the file. # Ensure that truncate(smaller than true size) shrinks the file.
newsize -= 1 newsize -= 1
f.seek(0) f.seek(42)
f.truncate(newsize) f.truncate(newsize)
expect(f.tell(), newsize) expect(f.tell(), 42) # else pointer moved
f.seek(0, 2)
expect(f.tell(), newsize) # else wasn't truncated
# XXX truncate(larger than true size) is ill-defined across platforms
# cut it waaaaay back # cut it waaaaay back
f.truncate(1)
f.seek(0) f.seek(0)
expect(len(f.read()), 1) f.truncate(1)
expect(f.tell(), 0) # else pointer moved
expect(len(f.read()), 1) # else wasn't truncated
f.close() f.close()
os.unlink(name) os.unlink(name)

View File

@ -415,46 +415,59 @@ file_truncate(PyFileObject *f, PyObject *args)
#ifdef MS_WIN32 #ifdef MS_WIN32
/* MS _chsize doesn't work if newsize doesn't fit in 32 bits, /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
so don't even try using it. truncate() should never grow the so don't even try using it. */
file, but MS SetEndOfFile will grow a file, so we need to
compare the specified newsize to the actual size. Some
optimization could be done here when newsizeobj is NULL. */
{ {
Py_off_t currentEOF; /* actual size */ Py_off_t current; /* current file position */
HANDLE hFile; HANDLE hFile;
int error; int error;
/* First move to EOF, and set currentEOF to the size. */ /* current <- current file postion. */
errno = 0; if (newsizeobj == NULL)
if (_portable_fseek(f->f_fp, 0, SEEK_END) != 0) current = newsize;
goto onioerror; else {
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 Py_BEGIN_ALLOW_THREADS
errno = 0; errno = 0;
hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp)); current = _portable_ftell(f->f_fp);
error = hFile == (HANDLE)-1; Py_END_ALLOW_THREADS
if (!error) { if (current == -1)
error = SetEndOfFile(hFile) == 0; goto onioerror;
if (error) }
errno = EACCES;
} /* Move to newsize. */
if (current != newsize) {
Py_BEGIN_ALLOW_THREADS
errno = 0;
error = _portable_fseek(f->f_fp, newsize, SEEK_SET)
!= 0;
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (error) if (error)
goto onioerror; goto onioerror;
} }
/* Truncate. Note that this may grow the file! */
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;
/* Restore original file position. */
if (current != newsize) {
Py_BEGIN_ALLOW_THREADS
errno = 0;
error = _portable_fseek(f->f_fp, current, SEEK_SET)
!= 0;
Py_END_ALLOW_THREADS
if (error)
goto onioerror;
}
} }
#else #else
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS