Skip to content

Commit

Permalink
fixes to chown
Browse files Browse the repository at this point in the history
  • Loading branch information
tomerfiliba committed Oct 6, 2012
1 parent 0577dd1 commit cb7bd87
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 310 deletions.
20 changes: 20 additions & 0 deletions plumbum/cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Module hack: ``from plumbum.cmd import ls``
"""
import sys
from types import ModuleType
from plumbum.local_machine import local

__all__ = []

class LocalModule(ModuleType):
"""The module-hack that allows us to use ``from plumbum.cmd import some_program``"""
def __init__(self, name):
ModuleType.__init__(self, name, __doc__)
self.__file__ = None
self.__package__ = ".".join(name.split(".")[:-1])
def __getattr__(self, name):
return local[name]

LocalModule = LocalModule("plumbum.cmd")
sys.modules[LocalModule.__name__] = LocalModule
2 changes: 1 addition & 1 deletion plumbum/lib.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import heapq


def _setdoc(super):
def _setdoc(super): #@ReservedAssignment
def deco(func):
func.__doc__ = getattr(getattr(super, func.__name__, None), "__doc__", None)
return func
Expand Down
85 changes: 32 additions & 53 deletions plumbum/local_machine.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
from __future__ import with_statement
import os
import sys
import grp
import pwd
import glob
import shutil
import subprocess
import logging
import stat
import time
from types import ModuleType
from tempfile import mkdtemp
from subprocess import Popen, PIPE
from contextlib import contextmanager

from plumbum.path import Path
from plumbum.path import Path, FSUser
from plumbum.remote_path import RemotePath
from plumbum.commands import CommandNotFound, ConcreteCommand
from plumbum.session import ShellSession
from plumbum.lib import _setdoc
import platform

try:
from pwd import getpwuid, getpwnam
from grp import getgrgid, getgrnam
except ImportError:
def getpwuid(x): return (None,)
def getgrgid(x): return (None,)
def getpwnam(x): raise OSError("`getpwnam` not supported")
def getgrnam(x): raise OSError("`getgrnam` not supported")

logger = logging.getLogger("plumbum.local")

Expand Down Expand Up @@ -60,23 +67,17 @@ def dirname(self):

@property
@_setdoc(Path)
def owner(self):
stat = os.stat(str(self))
return pwd.getpwuid(stat.st_uid)[0]

@owner.setter
def owner(self, owner):
self.chown(owner)
def uid(self):
uid = self.stat().st_uid
name = getpwuid(uid)[0]
return FSUser(uid, name)

@property
@_setdoc(Path)
def group(self):
stat = os.stat(str(self))
return grp.getgrgid(stat.st_gid)[0]

@group.setter
def group(self, group):
self.chown(group=group)
gid = self.stat().st_gid
name = getgrgid(gid)[0]
return FSUser(gid, name)

@_setdoc(Path)
def join(self, other):
Expand Down Expand Up @@ -157,22 +158,15 @@ def write(self, data):
f.write(data)

@_setdoc(Path)
def chown(self, owner='', group='', uid='', gid='', recursive=False):
gid = str(gid) # str so uid 0 (int) isn't seen as False
uid = str(uid)
args = list()
if recursive:
args.append('-R')
if uid:
owner = uid
if gid:
group = gid
if group:
owner = '%s:%s' % (owner, group)
args.append(owner)
args.append(str(self))
# recursive is a pain using os.chown
local['chown'](*args)
def chown(self, owner=None, group=None, recursive = None):
if not hasattr(os, "chown"):
raise OSError("os.chown() not supported")
uid = owner if isinstance(owner, int) else getpwnam(owner)[2]
gid = group if isinstance(group, int) else getgrnam(group)[2]
os.chown(str(self), uid, gid)
if recursive or (recursive is None and self.isdir()):
for subpath in self.walk():
os.chown(str(subpath), uid, gid)


class Workdir(LocalPath):
Expand Down Expand Up @@ -449,6 +443,7 @@ class LocalMachine(object):
cwd = Workdir()
env = LocalEnv()
encoding = sys.getfilesystemencoding()
uname = platform.uname()[0]

if IS_WIN32:
_EXTENSIONS = [""] + env.get("PATHEXT", ":.exe:.bat").lower().split(os.path.pathsep)
Expand Down Expand Up @@ -502,10 +497,9 @@ def which(cls, progname):
raise CommandNotFound(progname, list(cls.env.path))

def path(self, *parts):
"""A factory for :class:`LocalPaths <plumbum.local_machine.LocalPath>`. Usage
::
"""A factory for :class:`LocalPaths <plumbum.local_machine.LocalPath>`.
Usage ::
p = local.path("/usr", "lib", "python2.7")
"""
parts2 = [str(self.cwd)]
Expand Down Expand Up @@ -565,7 +559,7 @@ def session(self):
def tempdir(self):
"""A context manager that creates a temporary directory, which is removed when the context
exits"""
dir = self.path(mkdtemp())
dir = self.path(mkdtemp()) #@ReservedAssignment
try:
yield dir
finally:
Expand All @@ -586,18 +580,3 @@ def tempdir(self):
* ``encoding`` - the local machine's default encoding (``sys.getfilesystemencoding()``)
"""

#===================================================================================================
# Module hack: ``from plumbum.cmd import ls``
#===================================================================================================
class LocalModule(ModuleType):
"""The module-hack that allows us to use ``from plumbum.cmd import some_program``"""
def __init__(self, name):
ModuleType.__init__(self, name, __doc__)
self.__file__ = None
self.__package__ = ".".join(name.split(".")[:-1])
def __getattr__(self, name):
return local[name]

LocalModule = LocalModule("plumbum.cmd")
sys.modules[LocalModule.__name__] = LocalModule

35 changes: 29 additions & 6 deletions plumbum/path.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
class FSUser(int):
"""A special object that represents file-system user. It derives from ``int``, so it behaves
just like a number (``uid``/``gid``), but also have a ``.name`` attribute that holds the
string-name of the user, if given
"""
__slots__ = ["name"]
def __new__(cls, val, name = None):
self = int.__new__(cls, val)
self.name = name
return self

class Path(object):
"""An abstraction over file system paths. This class is abstract, and the two implementations
are :class:`LocalPath <plumbum.local_machine.LocalPath>` and
Expand Down Expand Up @@ -68,12 +79,16 @@ def dirname(self):
"""The dirname component of this path"""
raise NotImplementedError()
@property
def owner(self):
"""The owner of leaf component of this path"""
def uid(self):
"""The user that owns this path. The returned value is a :class:`FSUser <plumbum.path.FSUser>`
object which behaves like an ``int`` (as expected from ``uid``), but it also has a ``.name``
attribute that holds the string-name of the user"""
raise NotImplementedError()
@property
def group(self):
"""The group of leaf component of this path"""
def gid(self):
"""The group that owns this path. The returned value is a :class:`FSUser <plumbum.path.FSUser>`
object which behaves like an ``int`` (as expected from ``gid``), but it also has a ``.name``
attribute that holds the string-name of the group"""
raise NotImplementedError()

def _get_info(self):
Expand Down Expand Up @@ -122,6 +137,14 @@ def read(self):
def write(self, data):
"""writes the given data to this file"""
raise NotImplementedError()
def chown(self, owner=None, group=None, uid=None, gid=None, recursive=False):
"""Change ownership of leaf component of this path"""
def chown(self, owner=None, group=None, recursive=None):
"""Change ownership of this path.
:param owner: The owner to set (either ``uid`` or ``username``), optional
:param owner: The group to set (either ``gid`` or ``groupname``), optional
:param recursive: whether to change ownership of all contained files and subdirectories.
Only meaningful when ``self`` is a directory. If ``None``, the value
will default to ``True`` if ``self`` is a directory, ``False`` otherwise.
"""
raise NotImplementedError()

9 changes: 4 additions & 5 deletions plumbum/remote_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,9 @@ def close(self):
self._session = ClosedRemote(self)

def path(self, *parts):
"""A factory for :class:`RemotePaths <plumbum.remote_machine.RemotePath>`. Usage
::
"""A factory for :class:`RemotePaths <plumbum.remote_machine.RemotePath>`.
Usage ::
p = rem.path("/usr", "lib", "python2.7")
"""
parts2 = [str(self.cwd)]
Expand Down Expand Up @@ -274,7 +273,7 @@ def tempdir(self):
"""A context manager that creates a remote temporary directory, which is removed when
the context exits"""
_, out, _ = self._session.run("mktemp -d")
dir = self.path(out.strip())
dir = self.path(out.strip()) #@ReservedAssignment
try:
yield dir
finally:
Expand Down
50 changes: 21 additions & 29 deletions plumbum/remote_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import errno
import six
from tempfile import NamedTemporaryFile
from plumbum.path import Path
from plumbum.path import Path, FSUser
from plumbum.lib import _setdoc
from plumbum.commands import shquote

Expand Down Expand Up @@ -61,24 +61,15 @@ def dirname(self):

@property
@_setdoc(Path)
def owner(self):
files = self.remote._session.run("ls -a %s" % (self,))[1].splitlines()
stat = os.stat(str(self))
return pwd.getpwuid(stat.st_uid)[0]

@owner.setter
def owner(self, owner):
self.chown(owner)
def uid(self):
uid, name = self.remote._session.run("stat -c '%u,%U' " + shquote(self)).split(",")
return FSUser(int(uid), name)

@property
@_setdoc(Path)
def group(self):
stat = os.stat(str(self))
return grp.getgrgid(stat.st_gid)[0]

@group.setter
def group(self, group):
self.chown(group=group)
def gid(self):
gid, name = self.remote._session.run("stat -c '%g,%G' " + shquote(self)).split(",")
return FSUser(int(gid), name)

def _get_info(self):
return (self.remote, self._path)
Expand Down Expand Up @@ -183,17 +174,18 @@ def write(self, data):
self.remote.upload(f.name, self)

@_setdoc(Path)
def chown(self, owner='', group='', uid='', gid='', recursive=False):
gid = str(gid) # str so uid 0 (int) isn't seen as False
uid = str(uid)
args = list()
def chown(self, owner=None, group=None, recursive=None):
args = ["chown"]
if recursive is None:
recursive = self.isdir()
if recursive:
args.append('-R')
if uid:
owner = uid
if gid:
group = gid
if group:
owner = '%s:%s' % (owner, group)
args.append(owner)
self.remote._session.run('chown %s "%s"' % (' '.join(args), self))
args.append("-R")
if owner is not None and group is not None:
args.append("%s:%s" % (owner, group))
elif owner is not None:
args.append(str(owner))
elif group is not None:
args.append(":%s" % (group,))
args.append(shquote(self))
self.remote._session.run(" ".join(args))

2 changes: 1 addition & 1 deletion plumbum/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version = (1, 0, 0)
version_string = "1.0.0"
release_date = "2012.08.01"
release_date = "2012.10.06"
Loading

0 comments on commit cb7bd87

Please sign in to comment.