diff -r 94edc0f0002f Doc/library/pathlib.rst --- a/Doc/library/pathlib.rst Tue Feb 25 16:46:41 2014 -0800 +++ b/Doc/library/pathlib.rst Wed Feb 26 17:48:17 2014 +0800 @@ -640,6 +640,22 @@ 1327883547.852554 +.. method:: Path.chown(uid, gid) + + Change the file ownership, like :func:`os.chown`:: + + >>> p = Path('setup.py') + >>> p.stat().st_uid + 1000 + >>> p.stat().st_gid + 1000 + >>> p.chown(1, 20) + >>> p.stat().st_uid + 1 + >>> p.stat().st_gid + 20 + + .. method:: Path.chmod(mode) Change the file mode and permissions, like :func:`os.chmod`:: diff -r 94edc0f0002f Lib/pathlib.py --- a/Lib/pathlib.py Tue Feb 25 16:46:41 2014 -0800 +++ b/Lib/pathlib.py Wed Feb 26 17:48:17 2014 +0800 @@ -335,6 +335,12 @@ open = _wrap_strfunc(os.open) + if hasattr(os, "chown"): + chown = _wrap_strfunc(os.chown) + else: + def chown(self, pathobj, uid, gid): + raise NotImplementedError("chown() not available on this system") + listdir = _wrap_strfunc(os.listdir) chmod = _wrap_strfunc(os.chmod) @@ -1114,6 +1120,14 @@ self._raise_closed() self._accessor.chmod(self, mode) + def chown(self, uid, gid): + """ + Change the ownership of the path, like os.chown(). + """ + if self._closed: + self._raise_closed() + self._accessor.chown(self, uid, gid) + def lchmod(self, mode): """ Like chmod(), except if the path points to a symlink, the symlink's diff -r 94edc0f0002f Lib/test/support/__init__.py --- a/Lib/test/support/__init__.py Tue Feb 25 16:46:41 2014 -0800 +++ b/Lib/test/support/__init__.py Wed Feb 26 17:48:17 2014 +0800 @@ -99,6 +99,8 @@ "check_warnings", "EnvironmentVarGuard", "run_with_locale", "swap_item", "swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict", "run_with_tz", + # os + "root_in_posix", ] class Error(Exception): @@ -2174,3 +2176,16 @@ "memory allocations") import _testcapi return _testcapi.run_in_subinterp(code) + + +def root_in_posix(): + """ + Check whether we hold root account in posix or not. If it returns False, + it means either we are in non-posix system, such as Windows, or we are in + posix system but with non-root account. + """ + if hasattr(os, 'getuid'): + return os.getuid() == 0 + else: + # On Windows + return False diff -r 94edc0f0002f Lib/test/test_pathlib.py --- a/Lib/test/test_pathlib.py Tue Feb 25 16:46:41 2014 -0800 +++ b/Lib/test/test_pathlib.py Wed Feb 26 17:48:17 2014 +0800 @@ -7,10 +7,9 @@ import shutil import socket import stat -import sys +import getpass import tempfile import unittest -from contextlib import contextmanager from test import support TESTFN = support.TESTFN @@ -20,6 +19,21 @@ except ImportError: grp = pwd = None +if grp is not None: + # Get groups whom the process' user belongs to + groups = [g.gr_gid for g in grp.getgrall() if getpass.getuser() in g.gr_mem] + process_gid = os.getgid() + if process_gid not in groups: + groups.append(process_gid) +else: + groups = [] + +if pwd is not None: + # Get all users + all_users = [u.pw_uid for u in pwd.getpwall()] +else: + all_users = [] + class _BaseFlavourTest(object): @@ -1386,6 +1400,34 @@ self.assertRaises(ValueError, p.absolute) self.assertRaises(ValueError, p.__enter__) + @unittest.skipUnless(hasattr(os, 'chown') and len(groups) > 1, + "test needs os.chown and groups more than 1") + def test_chown_gid(self): + p = self.cls(BASE) / 'fileA' + gid_1, gid_2 = groups[0:2] + uid = p.stat().st_uid + p.chown(uid, gid_1) + gid = p.stat().st_gid + self.assertEqual(gid, gid_1) + p.chown(uid, gid_2) + gid = p.stat().st_gid + self.assertEqual(gid, gid_2) + + @unittest.skipUnless(hasattr(os, 'chown') and support.root_in_posix() and + len(all_users) > 1, + "test needs os.chown, root in posix, and users more " + "than 1") + def test_chown_uid_with_root(self): + p = self.cls(BASE) / 'fileA' + uid_1, uid_2 = all_users[0:2] + gid = p.stat().st_gid + p.chown(uid_1, gid) + uid = p.stat().st_uid + self.assertEqual(uid, uid_1) + p.chown(uid_2, gid) + uid = p.stat().st_uid + self.assertEqual(uid, uid_2) + def test_chmod(self): p = self.cls(BASE) / 'fileA' mode = p.stat().st_mode