]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport Tim's work on getting file.truncate working better on Win32.
authorMichael W. Hudson <mwh@python.net>
Sat, 16 Mar 2002 18:19:33 +0000 (18:19 +0000)
committerMichael W. Hudson <mwh@python.net>
Sat, 16 Mar 2002 18:19:33 +0000 (18:19 +0000)
"cvs diff | patch" managed to stick the NEWS item in the 2.2 final
section!  I wonder which silly man wrote patch <wink>.

Doc/lib/libstdtypes.tex
Lib/test/test_largefile.py
Misc/NEWS
Objects/fileobject.c

index 2ba87c4a9fed5c17bb163256e434324791695bae..2bd6420385890e363a92cb5d313b353e479b6ecd 100644 (file)
@@ -161,7 +161,7 @@ only by sequence types (below).
 
 \subsection{Numeric Types \label{typesnumeric}}
 
-There are four numeric types: \dfn{plain integers}, \dfn{long integers}, 
+There are four numeric types: \dfn{plain integers}, \dfn{long integers},
 \dfn{floating point numbers}, and \dfn{complex numbers}.
 Plain integers (also just called \dfn{integers})
 are implemented using \ctype{long} in C, which gives them at least 32
@@ -178,7 +178,7 @@ working with.
 
 Complex numbers have a real and imaginary part, which are both
 implemented using \ctype{double} in C.  To extract these parts from
-a complex number \var{z}, use \code{\var{z}.real} and \code{\var{z}.imag}.  
+a complex number \var{z}, use \code{\var{z}.real} and \code{\var{z}.imag}.
 
 Numbers are created by numeric literals or as the result of built-in
 functions and operators.  Unadorned integer literals (including hex
@@ -248,7 +248,7 @@ Notes:
 
 \item[(1)]
 For (plain or long) integer division, the result is an integer.
-The result is always rounded towards minus infinity: 1/2 is 0, 
+The result is always rounded towards minus infinity: 1/2 is 0,
 (-1)/2 is -1, 1/(-2) is -1, and (-1)/(-2) is 0.  Note that the result
 is a long integer if either operand is a long integer, regardless of
 the numeric value.
@@ -472,7 +472,7 @@ Notes:
   the end of the string: \code{len(\var{s}) + \var{i}} or
   \code{len(\var{s}) + \var{j}} is substituted.  But note that \code{-0} is
   still \code{0}.
-  
+
 \item[(3)] The slice of \var{s} from \var{i} to \var{j} is defined as
   the sequence of items with index \var{k} such that \code{\var{i} <=
   \var{k} < \var{j}}.  If \var{i} or \var{j} is greater than
@@ -808,7 +808,7 @@ are replaced by \code{\%g} conversions.\footnote{
 
 Additional string operations are defined in standard modules
 \refmodule{string}\refstmodindex{string} and
-\refmodule{re}.\refstmodindex{re} 
+\refmodule{re}.\refstmodindex{re}
 
 
 \subsubsection{XRange Type \label{typesseq-xrange}}
@@ -881,7 +881,7 @@ Notes:
   no longer works in Python 2.0.  Use of this misfeature has been
   deprecated since Python 1.4.
 
-\item[(2)] Raises an exception when \var{x} is not a list object.  The 
+\item[(2)] Raises an exception when \var{x} is not a list object.  The
   \method{extend()} method is experimental and not supported by
   mutable sequence types other than lists.
 
@@ -1034,7 +1034,7 @@ over a dictionary, as often used in set algorithms.
 
 File objects\obindex{file} are implemented using C's \code{stdio}
 package and can be created with the built-in constructor
-\function{file()}\bifuncindex{file} described in section 
+\function{file()}\bifuncindex{file} described in section
 \ref{built-in-funcs}, ``Built-in Functions.''\footnote{\function{file()}
 is new in Python 2.2.  The older built-in \function{open()} is an
 alias for \function{file()}.}
@@ -1100,10 +1100,10 @@ Files have the following methods:
 \begin{methoddesc}[file]{readline}{\optional{size}}
   Read one entire line from the file.  A trailing newline character is
   kept in the string\footnote{
-       The advantage of leaving the newline on is that an empty string 
-       can be returned to mean \EOF{} without being ambiguous.  Another 
-       advantage is that (in cases where it might matter, for example. if you 
-       want to make an exact copy of a file while scanning its lines) 
+       The advantage of leaving the newline on is that an empty string
+       can be returned to mean \EOF{} without being ambiguous.  Another
+       advantage is that (in cases where it might matter, for example. if you
+       want to make an exact copy of a file while scanning its lines)
        you can tell whether the last line of a file ended in a newline
        or not (yes this happens!).
   } (but may be absent when a file ends with an
@@ -1152,11 +1152,15 @@ Files have the following methods:
 \end{methoddesc}
 
 \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
-  defaults to the current position.  Availability of this function
-  depends on the operating system version (for example, not all
-  \UNIX{} versions support this operation).
+  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.
 \end{methoddesc}
 
 \begin{methoddesc}[file]{write}{str}
index abfee39e988a570e6e0833486aca48ddeb9f0eda..8bff5df45ed72c95d53c454aaeaafa29e1690bb4 100644 (file)
@@ -128,20 +128,35 @@ 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
 f.close()
 
-
-# XXX add tests for truncate if it exists
-# XXX has truncate ever worked on Windows? specifically on WinNT I get:
-#     "IOError: [Errno 13] Permission denied"
-##try:
-##      newsize = size - 10
-##      f.seek(newsize)
-##      f.truncate()
-##      expect(f.tell(), newsize)
-##      newsize = newsize - 1
-##      f.seek(0)
-##      f.truncate(newsize)
-##      expect(f.tell(), newsize)
-##except AttributeError:
-##      pass
+if hasattr(f, 'truncate'):
+    if test_support.verbose:
+        print 'try truncate'
+    f = open(name, 'r+b')
+    f.seek(0, 2)
+    expect(f.tell(), size+1)    # else we've lost track of the true size
+    # Cut it back via seek + truncate with no argument.
+    newsize = size - 10
+    f.seek(newsize)
+    f.truncate()
+    expect(f.tell(), newsize)   # else pointer moved
+    f.seek(0, 2)
+    expect(f.tell(), newsize)   # else wasn't truncated
+    # Ensure that truncate(smaller than true size) shrinks the file.
+    newsize -= 1
+    f.seek(42)
+    f.truncate(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
+    f.seek(0)
+    f.truncate(1)
+    expect(f.tell(), 0)         # else pointer moved
+    expect(len(f.read()), 1)    # else wasn't truncated
+
+    f.close()
 
 os.unlink(name)
index d88c26606fe5d411686235cc47357de797eec7af..3f941c42d3affdf6be2916b8bb00ec3eafbf3034 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -4,6 +4,12 @@ Release date: XXX
 
 Core and builtins
 
+- If you try to pickle an instance of a class that has __slots__ but
+  doesn't define or override __getstate__, a TypeError is now raised.
+  This is done by adding a bozo __getstate__ to the class that always
+  raises TypeError.  (Before, this would appear to be pickled, but the
+  state of the slots would be lost.)
+
 - PyErr_Display will provide file and line information for all exceptions
   that have an attribute print_file_and_line, not just SyntaxErrors. This
   fixes the bug that no proper line number is given for bad \x escapes.
@@ -21,6 +27,12 @@ Library
   arbitrary shell code can't be executed because a bogus URL was
   passed in.
 
+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).
+
 What's New in Python 2.2 final?
 Release date: 21-Dec-2001
 ===============================
index 1430eef7419580913eb785845dbc15621d37a779..138bdb63b32b130a73eb65ec97d27752351167de 100644 (file)
 
 #ifdef MS_WIN32
 #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 WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
 #endif
 
 #ifdef macintosh
@@ -375,6 +377,9 @@ file_truncate(PyFileObject *f, PyObject *args)
        newsizeobj = NULL;
        if (!PyArg_ParseTuple(args, "|O:truncate", &newsizeobj))
                return NULL;
+
+       /* Set newsize to current postion if newsizeobj NULL, else to the
+          specified value. */
        if (newsizeobj != NULL) {
 #if !defined(HAVE_LARGEFILE_SUPPORT)
                newsize = PyInt_AsLong(newsizeobj);
@@ -385,37 +390,80 @@ file_truncate(PyFileObject *f, PyObject *args)
 #endif
                if (PyErr_Occurred())
                        return NULL;
-       } else {
-               /* Default to current position*/
+       }
+       else {
+               /* Default to current position. */
                Py_BEGIN_ALLOW_THREADS
                errno = 0;
                newsize = _portable_ftell(f->f_fp);
                Py_END_ALLOW_THREADS
-               if (newsize == -1) {
-                       PyErr_SetFromErrno(PyExc_IOError);
-                       clearerr(f->f_fp);
-                       return NULL;
-               }
+               if (newsize == -1)
+                       goto onioerror;
        }
+
+       /* Flush the file. */
        Py_BEGIN_ALLOW_THREADS
        errno = 0;
        ret = fflush(f->f_fp);
        Py_END_ALLOW_THREADS
-       if (ret != 0) goto onioerror;
+       if (ret != 0)
+               goto onioerror;
 
 #ifdef MS_WIN32
-       /* can use _chsize; if, however, the newsize overflows 32-bits then
-          _chsize is *not* adequate; in this case, an OverflowError is raised */
-       if (newsize > LONG_MAX) {
-               PyErr_SetString(PyExc_OverflowError,
-                       "the new size is too long for _chsize (it is limited to 32-bit values)");
-               return NULL;
-       } else {
+       /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
+          so don't even try using it. */
+       {
+               Py_off_t current;       /* current file position */
+               HANDLE hFile;
+               int error;
+
+               /* current <- current file postion. */
+               if (newsizeobj == NULL)
+                       current = newsize;
+               else {
+                       Py_BEGIN_ALLOW_THREADS
+                       errno = 0;
+                       current = _portable_ftell(f->f_fp);
+                       Py_END_ALLOW_THREADS
+                       if (current == -1)
+                               goto onioerror;
+               }
+
+               /* 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
+                       if (error)
+                               goto onioerror;
+               }
+
+               /* Truncate.  Note that this may grow the file! */
                Py_BEGIN_ALLOW_THREADS
                errno = 0;
-               ret = _chsize(fileno(f->f_fp), (long)newsize);
+               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 (ret != 0) goto onioerror;
+               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
        Py_BEGIN_ALLOW_THREADS