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:
parent
9d142adfce
commit
8f01b680c8
@ -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}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user