From f607bdaa77475ec8c94614414dc2cecf8fd1ca0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= <martin@v.loewis.de>
Date: Wed, 16 Oct 2002 18:27:39 +0000
Subject: Add PyStructSequence_UnnamedField. Add stat_float_times. Use integers
 in stat tuple, optionally floats in named fields.

---
 Doc/lib/libos.tex           | 33 +++++++++++++++++--
 Doc/whatsnew/whatsnew23.tex | 25 ++++++++++++++
 Include/structseq.h         |  2 ++
 Modules/posixmodule.c       | 80 ++++++++++++++++++++++++++++++++++++++++++---
 Objects/structseq.c         | 29 ++++++++++------
 5 files changed, 151 insertions(+), 18 deletions(-)

diff --git a/Doc/lib/libos.tex b/Doc/lib/libos.tex
index 17b7c67..2b4728c 100644
--- a/Doc/lib/libos.tex
+++ b/Doc/lib/libos.tex
@@ -854,9 +854,10 @@ the \ctype{stat} structure, namely:
 \member{st_ctime}
 (time of most recent content modification or metadata change).
 
-\versionchanged [The time values are floats, measuring
-  seconds. Fractions of a second may be reported if the system
-  supports that]{2.3}
+\versionchanged [If \function{stat_float_times} returns true, the time
+values are floats, measuring seconds. Fractions of a second may be
+reported if the system supports that. On Mac OS, the times are always
+floats. See \function{stat_float_times} for further discussion. ]{2.3}
 
 On some Unix systems (such as Linux), the following attributes may
 also be available:
@@ -899,6 +900,32 @@ Availability: Macintosh, \UNIX, Windows.
 [Added access to values as attributes of the returned object]{2.2}
 \end{funcdesc}
 
+\begin{funcdesc}{stat_float_times}{\optional{newvalue}}
+Determine whether \class{stat_result} represents time stamps as float
+objects.  If newval is True, future calls to stat() return floats, if
+it is False, future calls return ints.  If newval is omitted, return
+the current setting.
+
+For compatibility with older Python versions, accessing
+\class{stat_result} as a tuple always returns integers. For
+compatibility with Python 2.2, accessing the time stamps by field name
+also returns integers. Applications that want to determine the
+fractions of a second in a time stamp can use this function to have
+time stamps represented as floats. Whether they will actually observe
+non-zero fractions depends on the system.
+
+Future Python releases will change the default of this settings;
+applications that cannot deal with floating point time stamps can then
+use this function to turn the feature off.
+
+It is recommended that this setting is only changed at program startup
+time in the \var{__main__} module; libraries should never change this
+setting. If an application uses a library that works incorrectly if
+floating point time stamps are processed, this application should turn
+the feature off until the library has been corrected.
+
+\end{funcdesc}
+
 \begin{funcdesc}{statvfs}{path}
 Perform a \cfunction{statvfs()} system call on the given path.  The
 return value is an object whose attributes describe the filesystem on
diff --git a/Doc/whatsnew/whatsnew23.tex b/Doc/whatsnew/whatsnew23.tex
index 85f6643..2b14698 100644
--- a/Doc/whatsnew/whatsnew23.tex
+++ b/Doc/whatsnew/whatsnew23.tex
@@ -1067,6 +1067,31 @@ in \module{xml.dom.minidom} can now generate XML output in a
 particular encoding, by specifying an optional encoding argument to
 the \method{toxml()} and \method{toprettyxml()} methods of DOM nodes.
 
+\item The \function{stat} family of functions can now report fractions
+of a second in a time stamp. Similar to \function{time.time}, such
+time stamps are represented as floats.
+
+During testing, it was found that some applications break if time
+stamps are floats. For compatibility, when using the tuple interface
+of the \class{stat_result}, time stamps are represented as integers.
+When using named fields (first introduced in Python 2.2), time stamps
+are still represented as ints, unless \function{os.stat_float_times}
+is invoked:
+
+\begin{verbatim}
+>>> os.stat_float_times(True)
+>>> os.stat("/tmp").st_mtime
+1034791200.6335014
+\end{verbatim}
+
+In Python 2.4, the default will change to return floats.
+
+Application developers should use this feature only if all their
+libraries work properly when confronted with floating point time
+stamps (or use the tuple API). If used, the feature should be
+activated on application level, instead of trying to activate it on a
+per-use basis.
+
 \end{itemize}
 
 
diff --git a/Include/structseq.h b/Include/structseq.h
index 67cd947..ee6a04e 100644
--- a/Include/structseq.h
+++ b/Include/structseq.h
@@ -19,6 +19,8 @@ typedef struct PyStructSequence_Desc {
 	int n_in_sequence;
 } PyStructSequence_Desc;
 
+extern char* PyStructSequence_UnnamedField;
+
 PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type, 
 						 PyStructSequence_Desc *desc);
        
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 0f2fbb9..53324b5 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -678,6 +678,10 @@ static PyStructSequence_Field stat_result_fields[] = {
 	{"st_uid",     "user ID of owner"},
 	{"st_gid",     "group ID of owner"},
 	{"st_size",    "total size, in bytes"},
+	/* The NULL is replaced with PyStructSequence_UnnamedField later. */
+	{NULL,   "integer time of last access"},
+	{NULL,   "integer time of last modification"},
+	{NULL,   "integer time of last change"},
 	{"st_atime",   "time of last access"},
 	{"st_mtime",   "time of last modification"},
 	{"st_ctime",   "time of last change"},
@@ -694,9 +698,9 @@ static PyStructSequence_Field stat_result_fields[] = {
 };
 
 #ifdef HAVE_ST_BLKSIZE
-#define ST_BLKSIZE_IDX 10
+#define ST_BLKSIZE_IDX 13
 #else
-#define ST_BLKSIZE_IDX 9
+#define ST_BLKSIZE_IDX 12
 #endif
 
 #ifdef HAVE_ST_BLOCKS
@@ -749,13 +753,73 @@ static PyStructSequence_Desc statvfs_result_desc = {
 
 static PyTypeObject StatResultType;
 static PyTypeObject StatVFSResultType;
+static newfunc structseq_new;
+
+static PyObject *
+statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	PyStructSequence *result;
+	int i;
+
+	result = (PyStructSequence*)structseq_new(type, args, kwds);
+	if (!result)
+		return NULL;
+	/* If we have been initialized from a tuple,
+	   st_?time might be set to None. Initialize it
+	   from the int slots.  */
+	for (i = 7; i <= 9; i++) {
+		if (result->ob_item[i+3] == Py_None) {
+			Py_DECREF(Py_None);
+			Py_INCREF(result->ob_item[i]);
+			result->ob_item[i+3] = result->ob_item[i];
+		}
+	}
+	return (PyObject*)result;
+}
+
+
+
+/* If true, st_?time is float. */
+static int _stat_float_times = 0;
+
+PyDoc_STRVAR(stat_float_times__doc__,
+"stat_float_times([newval]) -> oldval\n\n\
+Determine whether os.[lf]stat represents time stamps as float objects.\n\
+If newval is True, future calls to stat() return floats, if it is False,\n\
+future calls return ints. \n\
+If newval is omitted, return the current setting.\n");
+
+static PyObject*
+stat_float_times(PyObject* self, PyObject *args)
+{
+	int newval = -1;
+	if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval))
+		return NULL;
+	if (newval == -1)
+		/* Return old value */
+		return PyBool_FromLong(_stat_float_times);
+	_stat_float_times = newval;
+	Py_INCREF(Py_None);
+	return Py_None;
+}
 
 static void
 fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
 {
-	PyObject *val;
-        val = PyFloat_FromDouble(sec + 1e-9*nsec);
-	PyStructSequence_SET_ITEM(v, index, val);
+	PyObject *fval,*ival;
+#if SIZEOF_TIME_T > SIZEOF_LONG
+	ival = PyLong_FromLongLong((LONG_LONG)sec);
+#else
+	ival = PyInt_FromLong((long)sec);
+#endif
+	if (_stat_float_times) {
+		fval = PyFloat_FromDouble(sec + 1e-9*nsec);
+	} else {
+		fval = ival;
+		Py_INCREF(fval);
+	}
+	PyStructSequence_SET_ITEM(v, index, ival);
+	PyStructSequence_SET_ITEM(v, index+3, fval);
 }
 
 /* pack a system stat C structure into the Python stat tuple
@@ -6802,6 +6866,7 @@ static PyMethodDef posix_methods[] = {
 	{"rename",	posix_rename, METH_VARARGS, posix_rename__doc__},
 	{"rmdir",	posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
 	{"stat",	posix_stat, METH_VARARGS, posix_stat__doc__},
+	{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
 #ifdef HAVE_SYMLINK
 	{"symlink",	posix_symlink, METH_VARARGS, posix_symlink__doc__},
 #endif /* HAVE_SYMLINK */
@@ -7296,7 +7361,12 @@ INITFUNC(void)
 #endif
 
 	stat_result_desc.name = MODNAME ".stat_result";
+	stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
+	stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
+	stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
 	PyStructSequence_InitType(&StatResultType, &stat_result_desc);
+	structseq_new = StatResultType.tp_new;
+	StatResultType.tp_new = statresult_new;
 	Py_INCREF((PyObject*) &StatResultType);
 	PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType);
 
diff --git a/Objects/structseq.c b/Objects/structseq.c
index fdc56cb..0f5a012 100644
--- a/Objects/structseq.c
+++ b/Objects/structseq.c
@@ -8,6 +8,10 @@
 static char visible_length_key[] = "n_sequence_fields";
 static char real_length_key[] = "n_fields";
 
+/* Fields with this name have only a field index, not a field name. 
+   They are only allowed for indices < n_visible_fields. */
+char *PyStructSequence_UnnamedField = "unnamed field";
+
 #define VISIBLE_SIZE(op) ((op)->ob_size)
 #define VISIBLE_SIZE_TP(tp) PyInt_AsLong( \
                       PyDict_GetItemString((tp)->tp_dict, visible_length_key))
@@ -332,10 +336,12 @@ PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
 {
 	PyObject *dict;
 	PyMemberDef* members;
-	int n_members, i;
+	int n_members, n_unnamed_members, i, k;
 
+	n_unnamed_members = 0;
 	for (i = 0; desc->fields[i].name != NULL; ++i)
-		;
+		if (desc->fields[0].name == PyStructSequence_UnnamedField)
+			n_unnamed_members++;
 	n_members = i;
 
 	memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
@@ -345,17 +351,20 @@ PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
 		sizeof(PyObject*)*(n_members-1);
 	type->tp_itemsize = 0;
 
-	members = PyMem_NEW(PyMemberDef, n_members+1);
+	members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
 	
-	for (i = 0; i < n_members; ++i) {
-		members[i].name = desc->fields[i].name;
-		members[i].type = T_OBJECT;
-		members[i].offset = offsetof(PyStructSequence, ob_item)
+	for (i = k = 0; i < n_members; ++i) {
+		if (desc->fields[i].name == PyStructSequence_UnnamedField)
+			continue;
+		members[k].name = desc->fields[i].name;
+		members[k].type = T_OBJECT;
+		members[k].offset = offsetof(PyStructSequence, ob_item)
 		  + i * sizeof(PyObject*);
-		members[i].flags = READONLY;
-		members[i].doc = desc->fields[i].doc;
+		members[k].flags = READONLY;
+		members[k].doc = desc->fields[i].doc;
+		k++;
 	}
-	members[n_members].name = NULL;
+	members[k].name = NULL;
 
 	type->tp_members = members;
 
-- 
cgit v0.12

