summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/os.rst3
-rw-r--r--Doc/library/shutil.rst14
-rw-r--r--Lib/shutil.py31
-rw-r--r--Lib/test/test_shutil.py59
-rw-r--r--Misc/NEWS3
5 files changed, 110 insertions, 0 deletions
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 47e6a1b..0c5c40c 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -1530,6 +1530,9 @@ Files and Directories
Change the owner and group id of *path* to the numeric *uid* and *gid*. To leave
one of the ids unchanged, set it to -1.
+ See :func:`shutil.chown` for a higher-level function that accepts names in
+ addition to numeric ids.
+
Availability: Unix.
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
index 0d80913..d87e605 100644
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -183,6 +183,20 @@ Directory and files operations
Availability: Unix, Windows.
+.. function:: chown(path, user=None, group=None)
+
+ Change owner *user* and/or *group* of the given *path*.
+
+ *user* can be a system user name or a uid; the same applies to *group*. At
+ least one argument is required.
+
+ See also :func:`os.chown`, the underlying function.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. exception:: Error
This exception collects exceptions that are raised during a multi-file
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 2955b04..4b75262 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -790,3 +790,34 @@ elif os.name == 'nt':
total, free = nt._getdiskusage(path)
used = total - free
return _ntuple_diskusage(total, used, free)
+
+def chown(path, user=None, group=None):
+ """Change owner user and group of the given path.
+
+ user and group can be the uid/gid or the user/group names, and in that case,
+ they are converted to their respective uid/gid.
+ """
+
+ if user is None and group is None:
+ raise ValueError("user and/or group must be set")
+
+ _user = user
+ _group = group
+
+ # -1 means don't change it
+ if user is None:
+ _user = -1
+ # user can either be an int (the uid) or a string (the system username)
+ elif isinstance(user, str):
+ _user = _get_uid(user)
+ if _user is None:
+ raise LookupError("no such user: {!r}".format(user))
+
+ if group is None:
+ _group = -1
+ elif not isinstance(group, int):
+ _group = _get_gid(group)
+ if _group is None:
+ raise LookupError("no such group: {!r}".format(group))
+
+ os.chown(path, _user, _group)
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 792b6cd..9f61ef7 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -712,6 +712,65 @@ class TestShutil(unittest.TestCase):
self.assertGreaterEqual(usage.total, usage.used)
self.assertGreater(usage.total, usage.free)
+ @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
+ def test_chown(self):
+
+ # cleaned-up automatically by TestShutil.tearDown method
+ dirname = self.mkdtemp()
+ filename = tempfile.mktemp(dir=dirname)
+ write_file(filename, 'testing chown function')
+
+ with self.assertRaises(ValueError):
+ shutil.chown(filename)
+
+ with self.assertRaises(LookupError):
+ shutil.chown(filename, user='non-exising username')
+
+ with self.assertRaises(LookupError):
+ shutil.chown(filename, group='non-exising groupname')
+
+ with self.assertRaises(TypeError):
+ shutil.chown(filename, b'spam')
+
+ with self.assertRaises(TypeError):
+ shutil.chown(filename, 3.14)
+
+ uid = os.getuid()
+ gid = os.getgid()
+
+ def check_chown(path, uid=None, gid=None):
+ s = os.stat(filename)
+ if uid is not None:
+ self.assertEqual(uid, s.st_uid)
+ if gid is not None:
+ self.assertEqual(gid, s.st_gid)
+
+ shutil.chown(filename, uid, gid)
+ check_chown(filename, uid, gid)
+ shutil.chown(filename, uid)
+ check_chown(filename, uid)
+ shutil.chown(filename, user=uid)
+ check_chown(filename, uid)
+ shutil.chown(filename, group=gid)
+ check_chown(filename, gid)
+
+ shutil.chown(dirname, uid, gid)
+ check_chown(dirname, uid, gid)
+ shutil.chown(dirname, uid)
+ check_chown(dirname, uid)
+ shutil.chown(dirname, user=uid)
+ check_chown(dirname, uid)
+ shutil.chown(dirname, group=gid)
+ check_chown(dirname, gid)
+
+ user = pwd.getpwuid(uid)[0]
+ group = grp.getgrgid(gid)[0]
+ shutil.chown(filename, user, group)
+ check_chown(filename, uid, gid)
+ shutil.chown(dirname, user, group)
+ check_chown(dirname, uid, gid)
+
class TestMove(unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index cd3e731..b2d9e84 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1144,6 +1144,9 @@ Library
- Issue #9347: Fix formatting for tuples in argparse type= error messages.
+- Issue #12191: Added shutil.chown() to change user and/or group owner of a
+ given path also specifying their names.
+
Build
-----