Skip to content

Commit f607bda

Browse files
committed
Add PyStructSequence_UnnamedField. Add stat_float_times.
Use integers in stat tuple, optionally floats in named fields.
1 parent 5b1614d commit f607bda

File tree

5 files changed

+151
-18
lines changed

5 files changed

+151
-18
lines changed

Doc/lib/libos.tex

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -854,9 +854,10 @@ \subsection{Files and Directories \label{os-file-dir}}
854854
\member{st_ctime}
855855
(time of most recent content modification or metadata change).
856856

857-
\versionchanged [The time values are floats, measuring
858-
seconds. Fractions of a second may be reported if the system
859-
supports that]{2.3}
857+
\versionchanged [If \function{stat_float_times} returns true, the time
858+
values are floats, measuring seconds. Fractions of a second may be
859+
reported if the system supports that. On Mac OS, the times are always
860+
floats. See \function{stat_float_times} for further discussion. ]{2.3}
860861

861862
On some Unix systems (such as Linux), the following attributes may
862863
also be available:
@@ -899,6 +900,32 @@ \subsection{Files and Directories \label{os-file-dir}}
899900
[Added access to values as attributes of the returned object]{2.2}
900901
\end{funcdesc}
901902

903+
\begin{funcdesc}{stat_float_times}{\optional{newvalue}}
904+
Determine whether \class{stat_result} represents time stamps as float
905+
objects. If newval is True, future calls to stat() return floats, if
906+
it is False, future calls return ints. If newval is omitted, return
907+
the current setting.
908+
909+
For compatibility with older Python versions, accessing
910+
\class{stat_result} as a tuple always returns integers. For
911+
compatibility with Python 2.2, accessing the time stamps by field name
912+
also returns integers. Applications that want to determine the
913+
fractions of a second in a time stamp can use this function to have
914+
time stamps represented as floats. Whether they will actually observe
915+
non-zero fractions depends on the system.
916+
917+
Future Python releases will change the default of this settings;
918+
applications that cannot deal with floating point time stamps can then
919+
use this function to turn the feature off.
920+
921+
It is recommended that this setting is only changed at program startup
922+
time in the \var{__main__} module; libraries should never change this
923+
setting. If an application uses a library that works incorrectly if
924+
floating point time stamps are processed, this application should turn
925+
the feature off until the library has been corrected.
926+
927+
\end{funcdesc}
928+
902929
\begin{funcdesc}{statvfs}{path}
903930
Perform a \cfunction{statvfs()} system call on the given path. The
904931
return value is an object whose attributes describe the filesystem on

Doc/whatsnew/whatsnew23.tex

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,31 @@ \section{New and Improved Modules}
10671067
particular encoding, by specifying an optional encoding argument to
10681068
the \method{toxml()} and \method{toprettyxml()} methods of DOM nodes.
10691069

1070+
\item The \function{stat} family of functions can now report fractions
1071+
of a second in a time stamp. Similar to \function{time.time}, such
1072+
time stamps are represented as floats.
1073+
1074+
During testing, it was found that some applications break if time
1075+
stamps are floats. For compatibility, when using the tuple interface
1076+
of the \class{stat_result}, time stamps are represented as integers.
1077+
When using named fields (first introduced in Python 2.2), time stamps
1078+
are still represented as ints, unless \function{os.stat_float_times}
1079+
is invoked:
1080+
1081+
\begin{verbatim}
1082+
>>> os.stat_float_times(True)
1083+
>>> os.stat("/tmp").st_mtime
1084+
1034791200.6335014
1085+
\end{verbatim}
1086+
1087+
In Python 2.4, the default will change to return floats.
1088+
1089+
Application developers should use this feature only if all their
1090+
libraries work properly when confronted with floating point time
1091+
stamps (or use the tuple API). If used, the feature should be
1092+
activated on application level, instead of trying to activate it on a
1093+
per-use basis.
1094+
10701095
\end{itemize}
10711096

10721097

Include/structseq.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ typedef struct PyStructSequence_Desc {
1919
int n_in_sequence;
2020
} PyStructSequence_Desc;
2121

22+
extern char* PyStructSequence_UnnamedField;
23+
2224
PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type,
2325
PyStructSequence_Desc *desc);
2426

Modules/posixmodule.c

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ static PyStructSequence_Field stat_result_fields[] = {
678678
{"st_uid", "user ID of owner"},
679679
{"st_gid", "group ID of owner"},
680680
{"st_size", "total size, in bytes"},
681+
/* The NULL is replaced with PyStructSequence_UnnamedField later. */
682+
{NULL, "integer time of last access"},
683+
{NULL, "integer time of last modification"},
684+
{NULL, "integer time of last change"},
681685
{"st_atime", "time of last access"},
682686
{"st_mtime", "time of last modification"},
683687
{"st_ctime", "time of last change"},
@@ -694,9 +698,9 @@ static PyStructSequence_Field stat_result_fields[] = {
694698
};
695699

696700
#ifdef HAVE_ST_BLKSIZE
697-
#define ST_BLKSIZE_IDX 10
701+
#define ST_BLKSIZE_IDX 13
698702
#else
699-
#define ST_BLKSIZE_IDX 9
703+
#define ST_BLKSIZE_IDX 12
700704
#endif
701705

702706
#ifdef HAVE_ST_BLOCKS
@@ -749,13 +753,73 @@ static PyStructSequence_Desc statvfs_result_desc = {
749753

750754
static PyTypeObject StatResultType;
751755
static PyTypeObject StatVFSResultType;
756+
static newfunc structseq_new;
757+
758+
static PyObject *
759+
statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
760+
{
761+
PyStructSequence *result;
762+
int i;
763+
764+
result = (PyStructSequence*)structseq_new(type, args, kwds);
765+
if (!result)
766+
return NULL;
767+
/* If we have been initialized from a tuple,
768+
st_?time might be set to None. Initialize it
769+
from the int slots. */
770+
for (i = 7; i <= 9; i++) {
771+
if (result->ob_item[i+3] == Py_None) {
772+
Py_DECREF(Py_None);
773+
Py_INCREF(result->ob_item[i]);
774+
result->ob_item[i+3] = result->ob_item[i];
775+
}
776+
}
777+
return (PyObject*)result;
778+
}
779+
780+
781+
782+
/* If true, st_?time is float. */
783+
static int _stat_float_times = 0;
784+
785+
PyDoc_STRVAR(stat_float_times__doc__,
786+
"stat_float_times([newval]) -> oldval\n\n\
787+
Determine whether os.[lf]stat represents time stamps as float objects.\n\
788+
If newval is True, future calls to stat() return floats, if it is False,\n\
789+
future calls return ints. \n\
790+
If newval is omitted, return the current setting.\n");
791+
792+
static PyObject*
793+
stat_float_times(PyObject* self, PyObject *args)
794+
{
795+
int newval = -1;
796+
if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval))
797+
return NULL;
798+
if (newval == -1)
799+
/* Return old value */
800+
return PyBool_FromLong(_stat_float_times);
801+
_stat_float_times = newval;
802+
Py_INCREF(Py_None);
803+
return Py_None;
804+
}
752805

753806
static void
754807
fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
755808
{
756-
PyObject *val;
757-
val = PyFloat_FromDouble(sec + 1e-9*nsec);
758-
PyStructSequence_SET_ITEM(v, index, val);
809+
PyObject *fval,*ival;
810+
#if SIZEOF_TIME_T > SIZEOF_LONG
811+
ival = PyLong_FromLongLong((LONG_LONG)sec);
812+
#else
813+
ival = PyInt_FromLong((long)sec);
814+
#endif
815+
if (_stat_float_times) {
816+
fval = PyFloat_FromDouble(sec + 1e-9*nsec);
817+
} else {
818+
fval = ival;
819+
Py_INCREF(fval);
820+
}
821+
PyStructSequence_SET_ITEM(v, index, ival);
822+
PyStructSequence_SET_ITEM(v, index+3, fval);
759823
}
760824

761825
/* pack a system stat C structure into the Python stat tuple
@@ -6802,6 +6866,7 @@ static PyMethodDef posix_methods[] = {
68026866
{"rename", posix_rename, METH_VARARGS, posix_rename__doc__},
68036867
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
68046868
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__},
6869+
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
68056870
#ifdef HAVE_SYMLINK
68066871
{"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__},
68076872
#endif /* HAVE_SYMLINK */
@@ -7296,7 +7361,12 @@ INITFUNC(void)
72967361
#endif
72977362

72987363
stat_result_desc.name = MODNAME ".stat_result";
7364+
stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
7365+
stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
7366+
stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
72997367
PyStructSequence_InitType(&StatResultType, &stat_result_desc);
7368+
structseq_new = StatResultType.tp_new;
7369+
StatResultType.tp_new = statresult_new;
73007370
Py_INCREF((PyObject*) &StatResultType);
73017371
PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType);
73027372

Objects/structseq.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
static char visible_length_key[] = "n_sequence_fields";
99
static char real_length_key[] = "n_fields";
1010

11+
/* Fields with this name have only a field index, not a field name.
12+
They are only allowed for indices < n_visible_fields. */
13+
char *PyStructSequence_UnnamedField = "unnamed field";
14+
1115
#define VISIBLE_SIZE(op) ((op)->ob_size)
1216
#define VISIBLE_SIZE_TP(tp) PyInt_AsLong( \
1317
PyDict_GetItemString((tp)->tp_dict, visible_length_key))
@@ -332,10 +336,12 @@ PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
332336
{
333337
PyObject *dict;
334338
PyMemberDef* members;
335-
int n_members, i;
339+
int n_members, n_unnamed_members, i, k;
336340

341+
n_unnamed_members = 0;
337342
for (i = 0; desc->fields[i].name != NULL; ++i)
338-
;
343+
if (desc->fields[0].name == PyStructSequence_UnnamedField)
344+
n_unnamed_members++;
339345
n_members = i;
340346

341347
memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject));
@@ -345,17 +351,20 @@ PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
345351
sizeof(PyObject*)*(n_members-1);
346352
type->tp_itemsize = 0;
347353

348-
members = PyMem_NEW(PyMemberDef, n_members+1);
354+
members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1);
349355

350-
for (i = 0; i < n_members; ++i) {
351-
members[i].name = desc->fields[i].name;
352-
members[i].type = T_OBJECT;
353-
members[i].offset = offsetof(PyStructSequence, ob_item)
356+
for (i = k = 0; i < n_members; ++i) {
357+
if (desc->fields[i].name == PyStructSequence_UnnamedField)
358+
continue;
359+
members[k].name = desc->fields[i].name;
360+
members[k].type = T_OBJECT;
361+
members[k].offset = offsetof(PyStructSequence, ob_item)
354362
+ i * sizeof(PyObject*);
355-
members[i].flags = READONLY;
356-
members[i].doc = desc->fields[i].doc;
363+
members[k].flags = READONLY;
364+
members[k].doc = desc->fields[i].doc;
365+
k++;
357366
}
358-
members[n_members].name = NULL;
367+
members[k].name = NULL;
359368

360369
type->tp_members = members;
361370

0 commit comments

Comments
 (0)