Commit 98bf58f1 authored by Guido van Rossum's avatar Guido van Rossum

SF patch #462296: Add attributes to os.stat results; by Nick Mathewson.

This is a big one, touching lots of files.  Some of the platforms
aren't tested yet.  Briefly, this changes the return value of the
os/posix functions stat(), fstat(), statvfs(), fstatvfs(), and the
time functions localtime(), gmtime(), and strptime() from tuples into
pseudo-sequences.  When accessed as a sequence, they behave exactly as
before.  But they also have attributes like st_mtime or tm_year.  The
stat return value, moreover, has a few platform-specific attributes
that are not available through the sequence interface (because
everybody expects the sequence to have a fixed length, these couldn't
be added there).  If your platform's struct stat doesn't define
st_blksize, st_blocks or st_rdev, they won't be accessible from Python
either.

(Still missing is a documentation update.)
parent 8dd7adeb
/* Tuple object interface */
#ifndef Py_STRUCTSEQ_H
#define Py_STRUCTSEQ_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PyStructSequence_Field {
char *name;
char *doc;
} PyStructSequence_Field;
typedef struct PyStructSequence_Desc {
char *name;
char *doc;
struct PyStructSequence_Field *fields;
int n_in_sequence;
} PyStructSequence_Desc;
extern DL_IMPORT(void) PyStructSequence_InitType(PyTypeObject *type,
PyStructSequence_Desc *desc);
extern DL_IMPORT(PyObject *) PyStructSequence_New(PyTypeObject* type);
typedef struct {
PyObject_VAR_HEAD
PyObject *ob_item[1];
} PyStructSequence;
/* Macro, *only* to be used to fill in brand new objects */
#define PyStructSequence_SET_ITEM(op, i, v) \
(((PyStructSequence *)(op))->ob_item[i] = v)
#ifdef __cplusplus
}
#endif
#endif /* !Py_STRUCTSEQ_H */
......@@ -11,7 +11,6 @@ warnings.filterwarnings("ignore", "tmpnam", RuntimeWarning, __name__)
from test_support import TESTFN, run_unittest
class TemporaryFileTests(unittest.TestCase):
def setUp(self):
self.files = []
......@@ -61,10 +60,128 @@ class TemporaryFileTests(unittest.TestCase):
"test_os")
self.check_tempfile(os.tmpnam())
# Test attributes on return values from os.*stat* family.
class StatAttributeTests(unittest.TestCase):
def setUp(self):
os.mkdir(TESTFN)
self.fname = os.path.join(TESTFN, "f1")
f = open(self.fname, 'wb')
f.write("ABC")
f.close()
def tearDown(self):
os.unlink(self.fname)
os.rmdir(TESTFN)
def test_stat_attributes(self):
if not hasattr(os, "stat"):
return
import stat
result = os.stat(self.fname)
# Make sure direct access works
self.assertEquals(result[stat.ST_SIZE], 3)
self.assertEquals(result.st_size, 3)
import sys
# Make sure all the attributes are there
members = dir(result)
for name in dir(stat):
if name[:3] == 'ST_':
attr = name.lower()
self.assertEquals(getattr(result, attr),
result[getattr(stat, name)])
self.assert_(attr in members)
try:
result[200]
self.fail("No exception thrown")
except IndexError:
pass
# Make sure that assignment fails
try:
result.st_mode = 1
self.fail("No exception thrown")
except TypeError:
pass
try:
result.st_rdev = 1
self.fail("No exception thrown")
except TypeError:
pass
try:
result.parrot = 1
self.fail("No exception thrown")
except AttributeError:
pass
# Use the stat_result constructor with a too-short tuple.
try:
result2 = os.stat_result((10,))
self.fail("No exception thrown")
except TypeError:
pass
# Use the constructr with a too-long tuple.
try:
result2 = os.stat_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def test_statvfs_attributes(self):
if not hasattr(os, "statvfs"):
return
import statvfs
result = os.statvfs(self.fname)
# Make sure direct access works
self.assertEquals(result.f_bfree, result[statvfs.F_BFREE])
# Make sure all the attributes are there
members = dir(result)
for name in dir(statvfs):
if name[:2] == 'F_':
attr = name.lower()
self.assertEquals(getattr(result, attr),
result[getattr(statvfs, name)])
self.assert_(attr in members)
# Make sure that assignment really fails
try:
result.f_bfree = 1
self.fail("No exception thrown")
except TypeError:
pass
try:
result.parrot = 1
self.fail("No exception thrown")
except AttributeError:
pass
# Use the constructor with a too-short tuple.
try:
result2 = os.statvfs_result((10,))
self.fail("No exception thrown")
except TypeError:
pass
# Use the constructr with a too-long tuple.
try:
result2 = os.statvfs_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def test_main():
run_unittest(TemporaryFileTests)
run_unittest(StatAttributeTests)
if __name__ == "__main__":
test_main()
......@@ -25,6 +25,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* Mac module implementation */
#include "Python.h"
#include "structseq.h"
#include "ceval.h"
#include <stdio.h>
......@@ -460,11 +461,119 @@ mac_rmdir(self, args)
return mac_1str(args, rmdir);
}
static char stat_result__doc__[] =
"stat_result: Result from stat or lstat.\n\n\
This object may be accessed either as a tuple of\n\
(mode,ino,dev,nlink,uid,gid,size,atime,mtime,ctime)\n\
or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\
\n\
Macintosh: The fields st_rsize, st_creator, and st_type are available from\n\
os.xstat.\n\
\n\
See os.stat for more information.\n";
#define COMMON_STAT_RESULT_FIELDS \
{ "st_mode", "protection bits" }, \
{ "st_ino", "inode" }, \
{ "st_dev", "device" }, \
{ "st_nlink", "number of hard links" }, \
{ "st_uid", "user ID of owner" }, \
{ "st_gid", "group ID of owner" }, \
{ "st_size", "total size, in bytes" }, \
{ "st_atime", "time of last access" }, \
{ "st_mtime", "time of last modification" }, \
{ "st_ctime", "time of last change" },
static PyStructSequence_Field stat_result_fields[] = {
COMMON_STAT_RESULT_FIELDS
{0}
};
static PyStructSequence_Desc stat_result_desc = {
"stat_result",
stat_result__doc__,
stat_result_fields,
10
};
static PyTypeObject StatResultType;
#ifdef TARGET_API_MAC_OS8
static PyStructSequence_Field xstat_result_fields[] = {
COMMON_XSTAT_RESULT_FIELDS
{ "st_rsize" },
{ "st_creator" },
{ "st_type "},
{0}
};
static PyStructSequence_Desc xstat_result_desc = {
"xstat_result",
stat_result__doc__,
xstat_result_fields,
13
};
static PyTypeObject XStatResultType;
#endif
static PyObject *
_pystat_from_struct_stat(struct stat st, void* _mst)
{
PyObject *v;
#if TARGET_API_MAC_OS8
struct macstat *mst;
if (_mst != NULL)
v = PyStructSequence_New(&XStatResultType);
else
#endif
v = PyStructSequence_New(&StatResultType);
PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long)st.st_mode));
PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long)st.st_ino));
PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long)st.st_dev));
PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long)st.st_nlink));
PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long)st.st_uid));
PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long)st.st_gid));
PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong((long)st.st_size));
PyStructSequence_SET_ITEM(v, 7,
PyFloat_FromDouble((double)st.st_atime));
PyStructSequence_SET_ITEM(v, 8,
PyFloat_FromDouble((double)st.st_mtime));
PyStructSequence_SET_ITEM(v, 9,
PyFloat_FromDouble((double)st.st_ctime));
#if TARGET_API_MAC_OS8
if (_mst != NULL) {
mst = (struct macstat *) _mst;
PyStructSequence_SET_ITEM(v, 10,
PyInt_FromLong((long)mst->st_rsize));
PyStructSequence_SET_ITEM(v, 11,
PyString_FromStringAndSize(mst->st_creator,
4));
PyStructSequence_SET_ITEM(v, 12,
PyString_FromStringAndSize(mst->st_type,
4));
}
#endif
if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}
return v;
}
static PyObject *
mac_stat(self, args)
PyObject *self;
PyObject *args;
{
PyObject *v;
struct stat st;
char *path;
int res;
......@@ -475,17 +584,8 @@ mac_stat(self, args)
Py_END_ALLOW_THREADS
if (res != 0)
return mac_error();
return Py_BuildValue("(lllllllddd)",
(long)st.st_mode,
(long)st.st_ino,
(long)st.st_dev,
(long)st.st_nlink,
(long)st.st_uid,
(long)st.st_gid,
(long)st.st_size,
(double)st.st_atime,
(double)st.st_mtime,
(double)st.st_ctime);
return _pystat_from_struct_stat(st, NULL);
}
#ifdef WEHAVE_FSTAT
......@@ -504,17 +604,8 @@ mac_fstat(self, args)
Py_END_ALLOW_THREADS
if (res != 0)
return mac_error();
return Py_BuildValue("(lllllllddd)",
(long)st.st_mode,
(long)st.st_ino,
(long)st.st_dev,
(long)st.st_nlink,
(long)st.st_uid,
(long)st.st_gid,
(long)st.st_size,
(double)st.st_atime,
(double)st.st_mtime,
(double)st.st_ctime);
return _pystat_from_struct_stat(st, NULL);
}
#endif /* WEHAVE_FSTAT */
......@@ -545,20 +636,8 @@ mac_xstat(self, args)
Py_END_ALLOW_THREADS
if (res != 0)
return mac_error();
return Py_BuildValue("(llllllldddls#s#)",
(long)st.st_mode,
(long)st.st_ino,
(long)st.st_dev,
(long)st.st_nlink,
(long)st.st_uid,
(long)st.st_gid,
(long)st.st_size,
(double)st.st_atime,
(double)st.st_mtime,
(double)st.st_ctime,
(long)mst.st_rsize,
mst.st_creator, 4,
mst.st_type, 4);
return _pystat_from_struct_stat(st, (void*) &mst);
}
#endif
......@@ -766,4 +845,12 @@ initmac()
/* Initialize mac.error exception */
MacError = PyErr_NewException("mac.error", NULL, NULL);
PyDict_SetItemString(d, "error", MacError);
PyStructSequence_InitType(&StatResultType, &stat_result_desc);
PyDict_SetItemString(d, "stat_result", (PyObject*) &StatResultType);
#if TARGET_API_MAC_OS8
PyStructSequence_InitType(&XStatResultType, &xstat_result_desc);
PyDict_SetItemString(d, "xstat_result", (PyObject*) &XStatResultType);
#endif
}
......@@ -263,6 +263,7 @@ OBJECT_OBJS= \
Objects/rangeobject.o \
Objects/sliceobject.o \
Objects/stringobject.o \
Objects/structseq.o \
Objects/tupleobject.o \
Objects/typeobject.o \
Objects/weakrefobject.o \
......@@ -465,6 +466,7 @@ PYTHON_HEADERS= \
Include/rangeobject.h \
Include/sliceobject.h \
Include/stringobject.h \
Include/structseq.h \
Include/structmember.h \
Include/symtable.h \
Include/sysmodule.h \
......
......@@ -48,7 +48,17 @@ Extension modules
- readline now supports setting the startup_hook and the pre_event_hook.
- posix supports chroot and setgroups where available.
- os and posix supports chroot() and setgroups() where available. The
stat(), fstat(), statvfs() and fstatvfs() functions now return
"pseudo-sequences" -- the various fields can now be accessed as
attributes (e.g. os.stat("/").st_mtime) but for backwards
compatibility they also behave as a fixed-length sequence. Some
platform-specific fields (e.g. st_rdev) are only accessible as
attributes.
- time: localtime(), gmtime() and strptime() now return a
pseudo-sequence similar to the os.stat() return value, with
attributes like tm_year etc.
- Decompression objects in the zlib module now accept an optional
second parameter to decompress() that specifies the maximum amount
......
This diff is collapsed.
......@@ -2,6 +2,7 @@
/* Time module */
#include "Python.h"
#include "structseq.h"
#include <ctype.h>
......@@ -210,19 +211,53 @@ static char sleep_doc[] =
Delay execution for a given number of seconds. The argument may be\n\
a floating point number for subsecond precision.";
static PyStructSequence_Field struct_time_type_fields[] = {
{"tm_year", NULL},
{"tm_mon", NULL},
{"tm_mday", NULL},
{"tm_hour", NULL},
{"tm_min", NULL},
{"tm_sec", NULL},
{"tm_wday", NULL},
{"tm_yday", NULL},
{"tm_isdst", NULL},
{0}
};
static PyStructSequence_Desc struct_time_type_desc = {
"struct_time",
NULL,
struct_time_type_fields,
9,
};
static PyTypeObject StructTimeType;
static PyObject *
tmtotuple(struct tm *p)
{
return Py_BuildValue("(iiiiiiiii)",
p->tm_year + 1900,
p->tm_mon + 1, /* Want January == 1 */
p->tm_mday,
p->tm_hour,
p->tm_min,
p->tm_sec,
(p->tm_wday + 6) % 7, /* Want Monday == 0 */
p->tm_yday + 1, /* Want January, 1 == 1 */
p->tm_isdst);
PyObject *v = PyStructSequence_New(&StructTimeType);
if (v == NULL)
return NULL;
#define SET(i,val) PyStructSequence_SET_ITEM(v, i, PyInt_FromLong((long) val))
SET(0, p->tm_year + 1900);
SET(1, p->tm_mon + 1); /* Want January == 1 */
SET(2, p->tm_mday);
SET(3, p->tm_hour);
SET(4, p->tm_min);
SET(5, p->tm_sec);
SET(6, (p->tm_wday + 6) % 7); /* Want Monday == 0 */
SET(7, p->tm_yday + 1); /* Want January, 1 == 1 */
SET(8, p->tm_isdst);
#undef SET
if (PyErr_Occurred()) {
Py_XDECREF(v);
return NULL;
}
return v;
}
static PyObject *
......@@ -674,6 +709,9 @@ inittime(void)
ins(d, "tzname", Py_BuildValue("(zz)", _tzname[0], _tzname[1]));
#endif /* __CYGWIN__ */
#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
PyStructSequence_InitType(&StructTimeType, &struct_time_type_desc);
PyDict_SetItemString(d, "struct_time", (PyObject*) &StructTimeType);
}
......@@ -852,5 +890,6 @@ floatsleep(double secs)
#endif /* !__WATCOMC__ || __QNX__ */
#endif /* !macintosh */
#endif /* !HAVE_SELECT */
return 0;
}
......@@ -113,8 +113,46 @@ static PyObject *riscos_listdir(PyObject *self,PyObject *args)
return d;
}
static char stat_result__doc__[] =
"stat_result: Result from stat or lstat.\n\n\
This object may be accessed either as a tuple of\n\
(mode,ino,dev,nlink,uid,gid,size,atime,mtime,ctime)\n\
or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\
\n\
RiscOS: The fields st_ftype, st_attrs, and st_obtype are also available.\n\
\n\
See os.stat for more information.\n";
static PyStructSequence_Field stat_result_fields[] = {
{ "st_mode", "protection bits" },
{ "st_ino", "inode" },
{ "st_dev", "device" },
{ "st_nlink", "number of hard links" },
{ "st_uid", "user ID of owner" },
{ "st_gid", "group ID of owner" },
{ "st_size", "total size, in bytes" },
{ "st_atime", "time of last access" },
{ "st_mtime", "time of last modification" },
{ "st_ctime", "time of last change" },
{ "st_ftype", "file type" },
{ "st_attrs", "attributes" },
{ "st_obtype", "object type" }
{ 0 }
};
static PyStructSequence_Desc stat_result_desc = {
"stat_result",
stat_result__doc__,
stat_result_fields,
13
};
static PyTypeObject StatResultType;
static PyObject *riscos_stat(PyObject *self,PyObject *args)
{ char *path;
{
PyObject *v;
char *path;
int ob,len;
bits t=0;
bits ld,ex,at,ft,mode;
......@@ -130,21 +168,34 @@ static PyObject *riscos_stat(PyObject *self,PyObject *args)
if(ft!=-1) t=unixtime(ld,ex);
mode|=(at&7)<<6;
mode|=((at&112)*9)>>4;
return Py_BuildValue("(lllllllllllll)",
(long)mode,/*st_mode*/
0,/*st_ino*/
0,/*st_dev*/
0,/*st_nlink*/
0,/*st_uid*/
0,/*st_gid*/
(long)len,/*st_size*/
(long)t,/*st_atime*/
(long)t,/*st_mtime*/
(long)t,/*st_ctime*/
(long)ft,/*file type*/
(long)at,/*attributes*/
(long)ob/*object type*/
);
v = PyStructSequence_New(&StatResultType);
PyStructSequence_SET_ITEM(v, 0,
PyInt_FromLong((long) mode)); /*st_mode*/
PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) 0)); /*st_ino*/
PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long) 0)); /*st_dev*/
PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long) 0)); /*st_nlink*/
PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long) 0)); /*st_uid*/
PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long) 0)); /*st_gid*/
PyStructSequence_SET_ITEM(v, 6,
PyInt_FromLong((long) len)); /*st_size*/
PyStructSequence_SET_ITEM(v, 7, PyInt_FromLong((long) t)); /*st_atime*/
PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) t)); /*st_mtime*/
PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) t)); /*st_ctime*/
PyStructSequence_SET_ITEM(v, 10,
PyInt_FromLong((long) ft)); /*file type*/
PyStructSequence_SET_ITEM(v, 11,
PyInt_FromLong((long) at)); /*attributes*/
PyStructSequence_SET_ITEM(v, 12,
PyInt_FromLong((long) ot)); /*object type*/
if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}
return v;
}
static PyObject *riscos_chmod(PyObject *self,PyObject *args)
......@@ -271,7 +322,7 @@ static PyMethodDef riscos_methods[] = {
void
initriscos()
{
PyObject *m, *d;
PyObject *m, *d, *stat_m;
m = Py_InitModule("riscos", riscos_methods);
d = PyModule_GetDict(m);
......@@ -280,4 +331,7 @@ initriscos()
RiscosError = PyString_FromString("riscos.error");
if (RiscosError == NULL || PyDict_SetItemString(d, "error", RiscosError) != 0)
Py_FatalError("can't define riscos.error");
PyStructSequence_InitType(&StatResultType, &stat_result_desc);
PyDict_SetItemString(d, "stat_result", (PyObject*) &StatResultType);
}
This diff is collapsed.
......@@ -1534,6 +1534,9 @@ AC_CHECK_FUNCS(getnameinfo)
AC_HEADER_TIME
AC_STRUCT_TM
AC_STRUCT_TIMEZONE
AC_STRUCT_ST_RDEV
AC_STRUCT_ST_BLKSIZE
AC_STRUCT_ST_BLOCKS
AC_MSG_CHECKING(for time.h that defines altzone)
AC_CACHE_VAL(ac_cv_header_time_altzone,
......
......@@ -18,6 +18,15 @@
/* Define to `int' if <sys/types.h> doesn't define. */
#undef gid_t
/* Define if your struct stat has st_blksize. */
#undef HAVE_ST_BLKSIZE
/* Define if your struct stat has st_blocks. */
#undef HAVE_ST_BLOCKS
/* Define if your struct stat has st_rdev. */
#undef HAVE_ST_RDEV
/* Define if your struct tm has tm_zone. */
#undef HAVE_TM_ZONE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment