diff -r 588fe0fc7160 Doc/library/shutil.rst --- a/Doc/library/shutil.rst Fri Jul 01 02:57:33 2011 +0200 +++ b/Doc/library/shutil.rst Fri Jul 01 10:20:32 2011 +0200 @@ -164,6 +164,14 @@ If the destination is on the current filesystem, then simply use rename. Otherwise, copy src (with :func:`copy2`) to the dst and then remove src. +.. function:: disk_usage(path) + + Return disk usage statistics about the given path as a namedtuple including + total, used and free space expressed in bytes. + + .. versionadded:: 3.3 + + Availability: Unix, Windows. .. exception:: Error diff -r 588fe0fc7160 Lib/shutil.py --- a/Lib/shutil.py Fri Jul 01 02:57:33 2011 +0200 +++ b/Lib/shutil.py Fri Jul 01 10:20:32 2011 +0200 @@ -12,6 +12,7 @@ import collections import errno import tarfile +from collections import namedtuple try: import bz2 @@ -754,3 +755,21 @@ func = _UNPACK_FORMATS[format][1] kwargs = dict(_UNPACK_FORMATS[format][2]) func(filename, extract_dir, **kwargs) + +if hasattr(os, "statvfs") or os.name == 'nt': + _ntuple_diskusage = namedtuple('usage', 'total used free') + + def disk_usage(path): + """Return disk usage statistics about the given path as a namedtuple + including total, used and free space expressed in bytes. + """ + if hasattr(os, "statvfs"): + st = os.statvfs(path) + free = st.f_bavail * st.f_bsize + total = st.f_blocks * st.f_frsize + used = (total - st.f_bfree * st.f_bsize) + else: + import nt + total, free = nt._getdiskusage(path) + used = total - free + return _ntuple_diskusage(total, used, free) diff -r 588fe0fc7160 Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py Fri Jul 01 02:57:33 2011 +0200 +++ b/Lib/test/test_shutil.py Fri Jul 01 10:20:32 2011 +0200 @@ -728,6 +728,16 @@ unregister_unpack_format('Boo2') self.assertEqual(get_unpack_formats(), formats) + @unittest.skipUnless(hasattr(shutil, 'disk_usage'), + "disk_usage not available on this platform") + def test_disk_usage(self): + usage = shutil.disk_usage(os.getcwd()) + self.assertTrue(usage.total > 0) + self.assertTrue(usage.used > 0) + self.assertTrue(usage.free >= 0) + self.assertTrue(usage.total >= usage.used) + self.assertTrue(usage.total > usage.free) + class TestMove(unittest.TestCase): diff -r 588fe0fc7160 Modules/posixmodule.c --- a/Modules/posixmodule.c Fri Jul 01 02:57:33 2011 +0200 +++ b/Modules/posixmodule.c Fri Jul 01 10:20:32 2011 +0200 @@ -7451,6 +7451,32 @@ } #endif /* HAVE_STATVFS */ +#ifdef MS_WINDOWS +PyDoc_STRVAR(win32__getdiskusage__doc__, +"_getdiskusage(path) -> (total, free)\n\n\ +Return disk usage statistics about the given path as (total, free) tuple."); + +static PyObject * +win32__getdiskusage(PyObject *self, PyObject *args) +{ + BOOL retval; + ULARGE_INTEGER _, total, free; + LPCTSTR path; + + if (! PyArg_ParseTuple(args, "s", &path)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + retval = GetDiskFreeSpaceEx(path, &_, &total, &free); + Py_END_ALLOW_THREADS + if (retval == 0) + return PyErr_SetFromWindowsErr(0); + + return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); +} +#endif + + /* This is used for fpathconf(), pathconf(), confstr() and sysconf(). * It maps strings representing configuration variable names to * integer values, allowing those functions to be called with the @@ -9716,6 +9742,7 @@ {"_getfinalpathname", posix__getfinalpathname, METH_VARARGS, NULL}, {"_getfileinformation", posix__getfileinformation, METH_VARARGS, NULL}, {"_isdir", posix__isdir, METH_VARARGS, posix__isdir__doc__}, + {"_getdiskusage", win32__getdiskusage, METH_VARARGS, win32__getdiskusage__doc__}, #endif #ifdef HAVE_GETLOADAVG {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},