Tigger Level-5 <a.wild.tig...@gmail.com> added the comment:
Hi Éric,
Apologies for the late response, attached is the patch. Let me know if
I need any changes or anything else.
Best,
Tigger
On Sat, Nov 5, 2011 at 11:01 AM, Éric Araujo <rep...@bugs.python.org> wrote:
>
> Éric Araujo <mer...@netwok.org> added the comment:
>
> Tigger: Could you provide a patch for Python 3.3? (more info at
> http://docs.python.org/devguide/)
>
> ----------
>
> _______________________________________
> Python tracker <rep...@bugs.python.org>
> <http://bugs.python.org/issue13033>
> _______________________________________
----------
keywords: +patch
Added file: http://bugs.python.org/file24091/chowntree.patch
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue13033>
_______________________________________
diff -r 2b47f0146639 Doc/library/shutil.rst
--- a/Doc/library/shutil.rst Sun Sep 25 17:36:31 2011 +0200
+++ b/Doc/library/shutil.rst Mon Sep 26 17:18:43 2011 -0500
@@ -196,6 +196,33 @@
.. versionadded:: 3.3
+.. function:: chowntree(path, user=None, group=None, followlinks=False)
+
+ Change owner *user* and/or *group* of the given *path* and all contents
recursively.
+
+ *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` and :func:`os.walk`, the underlying functions.
+
+ By default, :func:`walk` will not walk down into symbolic links that
resolve to
+ directories. Set *followlinks* to ``True`` to visit directories pointed to
by
+ symlinks, on systems that support them.
+
+ .. note::
+
+ Be aware that setting *followlinks* to ``True`` can lead to infinite
recursion if a
+ link points to a parent directory of itself. This is due to the fact
that the underlying
+ function :func:`os.walk` does not keep track of
+ the directories it visited already.
+
+ .. note::
+
+ If there is an error with the underlying :func:`os.walk` method, then
any files whose
+ ownership was successfully changed will be reverted back to the original
ownership.
+
+ Availability: Unix.
+
.. exception:: Error
diff -r 2b47f0146639 Lib/shutil.py
--- a/Lib/shutil.py Sun Sep 25 17:36:31 2011 +0200
+++ b/Lib/shutil.py Mon Sep 26 17:18:43 2011 -0500
@@ -822,3 +822,57 @@
raise LookupError("no such group: {!r}".format(group))
os.chown(path, _user, _group)
+
+
+def chowntree(path, user=None, group=None, followlinks=False):
+ """Change owner user and group of the given path and all contents
recursively.
+
+ 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. followlinks tells us
whether we should
+ follow symlinks. This may lead to infinite recursion if links loop back.
+
+ The dictionary _modified_items, will keep track of the old ownership
details,
+ with keys being the full path, and values being tuples where the first
element is
+ the uid and the second element is the gid. This way if there are any
errors that
+ arise, we can undo any ownership changes to the old state.
+
+ """
+
+ 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))
+
+
+ _modified_items = dict()
+ def _listdir_errorhandler(oserror):
+ for path, stat in _modified_items.items():
+ os.chown(path, stat.st_uid, stat.st_gid)
+ raise oserror
+
+ if(os.path.isdir(path)):
+ for root, dirs, files in os.walk(path, onerror =
_listdir_errorhandler, followlinks = followlinks):
+ for item in (files + dirs):
+ _full_file_path = os.path.join(root, item)
+
+ stat_info = os.stat(_full_file_path)
+ os.chown(_full_file_path, _user, _group)
+ _modified_items[_full_file_path] = (stat_info.st_uid,
stat_info.st_gid)
+
+ os.chown(path, _user, _group)
diff -r 2b47f0146639 Lib/test/test_shutil.py
--- a/Lib/test/test_shutil.py Sun Sep 25 17:36:31 2011 +0200
+++ b/Lib/test/test_shutil.py Mon Sep 26 17:18:43 2011 -0500
@@ -770,6 +770,50 @@
check_chown(filename, uid, gid)
shutil.chown(dirname, user, group)
check_chown(dirname, uid, gid)
+
+
+ @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
+ def test_chowntree(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.chowntree(filename)
+
+ with self.assertRaises(LookupError):
+ shutil.chowntree(filename, user='non-exising username')
+
+ with self.assertRaises(LookupError):
+ shutil.chowntree(filename, group='non-exising groupname')
+
+ with self.assertRaises(TypeError):
+ shutil.chowntree(filename, b'spam')
+
+ with self.assertRaises(TypeError):
+ shutil.chowntree(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.chowntree(filename, uid, gid)
+ check_chown(filename, uid, gid)
+ shutil.chowntree(filename, uid)
+ check_chown(filename, uid)
+ shutil.chowntree(filename, user=uid)
+ check_chown(filename, uid)
+ shutil.chowntree(filename, group=gid)
+ check_chown(filename, gid=gid)
class TestMove(unittest.TestCase):
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com