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

Reply via email to