Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Merge branch 'main' into gh-64978-add-pathlib-path-chown
  • Loading branch information
barneygale committed May 7, 2023
commit ecb037e7038609acaa7b33b3f11eb758aafab8f2
205 changes: 31 additions & 174 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,180 +747,6 @@ def stat(self, *, follow_symlinks=True):
"""
return os.stat(self, follow_symlinks=follow_symlinks)

def owner(self):
"""
Return the login name of the file owner.
"""
try:
import pwd
return pwd.getpwuid(self.stat().st_uid).pw_name
except ImportError:
raise NotImplementedError("Path.owner() is unsupported on this system")

def group(self):
"""
Return the group name of the file gid.
"""

try:
import grp
return grp.getgrgid(self.stat().st_gid).gr_name
except ImportError:
raise NotImplementedError("Path.group() is unsupported on this system")

def open(self, mode='r', buffering=-1, encoding=None,
errors=None, newline=None):
"""
Open the file pointed by this path and return a file object, as
the built-in open() function does.
"""
if "b" not in mode:
encoding = io.text_encoding(encoding)
return io.open(self, mode, buffering, encoding, errors, newline)

def read_bytes(self):
"""
Open the file in bytes mode, read it, and close the file.
"""
with self.open(mode='rb') as f:
return f.read()

def read_text(self, encoding=None, errors=None):
"""
Open the file in text mode, read it, and close the file.
"""
encoding = io.text_encoding(encoding)
with self.open(mode='r', encoding=encoding, errors=errors) as f:
return f.read()

def write_bytes(self, data):
"""
Open the file in bytes mode, write to it, and close the file.
"""
# type-check for the buffer interface before truncating the file
view = memoryview(data)
with self.open(mode='wb') as f:
return f.write(view)

def write_text(self, data, encoding=None, errors=None, newline=None):
"""
Open the file in text mode, write to it, and close the file.
"""
if not isinstance(data, str):
raise TypeError('data must be str, not %s' %
data.__class__.__name__)
encoding = io.text_encoding(encoding)
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
return f.write(data)

def readlink(self):
"""
Return the path to which the symbolic link points.
"""
if not hasattr(os, "readlink"):
raise NotImplementedError("os.readlink() not available on this system")
return type(self)(os.readlink(self))

def touch(self, mode=0o666, exist_ok=True):
"""
Create this file with the given access mode, if it doesn't exist.
"""

if exist_ok:
# First try to bump modification time
# Implementation note: GNU touch uses the UTIME_NOW option of
# the utimensat() / futimens() functions.
try:
os.utime(self, None)
except OSError:
# Avoid exception chaining
pass
else:
return
flags = os.O_CREAT | os.O_WRONLY
if not exist_ok:
flags |= os.O_EXCL
fd = os.open(self, flags, mode)
os.close(fd)

def mkdir(self, mode=0o777, parents=False, exist_ok=False):
"""
Create a new directory at this given path.
"""
try:
os.mkdir(self, mode)
except FileNotFoundError:
if not parents or self.parent == self:
raise
self.parent.mkdir(parents=True, exist_ok=True)
self.mkdir(mode, parents=False, exist_ok=exist_ok)
except OSError:
# Cannot rely on checking for EEXIST, since the operating system
# could give priority to other errors like EACCES or EROFS
if not exist_ok or not self.is_dir():
raise

def chmod(self, mode, *, follow_symlinks=True):
"""
Change the permissions of the path, like os.chmod().
"""
os.chmod(self, mode, follow_symlinks=follow_symlinks)

def lchmod(self, mode):
"""
Like chmod(), except if the path points to a symlink, the symlink's
permissions are changed, rather than its target's.
"""
self.chmod(mode, follow_symlinks=False)

if hasattr(os, 'chown'):
def chown(self, owner=None, group=None):
"""Change the owner and/or group of the path.

owner and group can be the uid/gid or the user/group names, and
in that case, they are converted to their respective uid/gid.
"""
sys.audit('pathlib.Path.chown', self, owner, group)

if owner is None:
if group is None:
raise ValueError("user and/or group must be set")
owner = -1
elif isinstance(owner, str):
try:
import pwd
owner = pwd.getpwnam(owner)[2]
except (ImportError, KeyError):
raise LookupError(f"no such user: {owner!r}") from None

if group is None:
group = -1
elif isinstance(group, str):
try:
import grp
group = grp.getgrnam(group)[2]
except (ImportError, KeyError):
raise LookupError(f"no such group: {group!r}") from None

os.chown(self, owner, group)

def unlink(self, missing_ok=False):
"""
Remove this file or link.
If the path is a directory, use rmdir() instead.
"""
try:
os.unlink(self)
except FileNotFoundError:
if not missing_ok:
raise

def rmdir(self):
"""
Remove this directory. The directory must be empty.
"""
os.rmdir(self)

def lstat(self):
"""
Like stat(), except if the path points to a symlink, the symlink's
Expand Down Expand Up @@ -1415,6 +1241,37 @@ def lchmod(self, mode):
"""
self.chmod(mode, follow_symlinks=False)

if hasattr(os, 'chown'):
def chown(self, owner=None, group=None):
"""Change the owner and/or group of the path.

owner and group can be the uid/gid or the user/group names, and
in that case, they are converted to their respective uid/gid.
"""
sys.audit('pathlib.Path.chown', self, owner, group)

if owner is None:
if group is None:
raise ValueError("user and/or group must be set")
owner = -1
elif isinstance(owner, str):
try:
import pwd
owner = pwd.getpwnam(owner)[2]
except (ImportError, KeyError):
raise LookupError(f"no such user: {owner!r}") from None

if group is None:
group = -1
elif isinstance(group, str):
try:
import grp
group = grp.getgrnam(group)[2]
except (ImportError, KeyError):
raise LookupError(f"no such group: {group!r}") from None

os.chown(self, owner, group)

def unlink(self, missing_ok=False):
"""
Remove this file or link.
Expand Down
You are viewing a condensed version of this merge commit. You can view the full changes here.