Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 00002e6

Browse files
authored
bpo-39682: make pathlib.Path immutable by removing (undocumented) support for "closing" a path by using it as a context manager (GH-18846)
Support for using a path as a context manager remains, and is now a no-op.
1 parent 975ac32 commit 00002e6

File tree

3 files changed

+21
-49
lines changed

3 files changed

+21
-49
lines changed

Lib/pathlib.py

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,6 @@ class Path(PurePath):
10411041
"""
10421042
__slots__ = (
10431043
'_accessor',
1044-
'_closed',
10451044
)
10461045

10471046
def __new__(cls, *args, **kwargs):
@@ -1058,7 +1057,6 @@ def _init(self,
10581057
# Private non-constructor arguments
10591058
template=None,
10601059
):
1061-
self._closed = False
10621060
if template is not None:
10631061
self._accessor = template._accessor
10641062
else:
@@ -1071,15 +1069,18 @@ def _make_child_relpath(self, part):
10711069
return self._from_parsed_parts(self._drv, self._root, parts)
10721070

10731071
def __enter__(self):
1074-
if self._closed:
1075-
self._raise_closed()
10761072
return self
10771073

10781074
def __exit__(self, t, v, tb):
1079-
self._closed = True
1080-
1081-
def _raise_closed(self):
1082-
raise ValueError("I/O operation on closed path")
1075+
# https://bugs.python.org/issue39682
1076+
# In previous versions of pathlib, this method marked this path as
1077+
# closed; subsequent attempts to perform I/O would raise an IOError.
1078+
# This functionality was never documented, and had the effect of
1079+
# making Path objects mutable, contrary to PEP 428. In Python 3.9 the
1080+
# _closed attribute was removed, and this method made a no-op.
1081+
# This method and __enter__()/__exit__() should be deprecated and
1082+
# removed in the future.
1083+
pass
10831084

10841085
def _opener(self, name, flags, mode=0o666):
10851086
# A stub for the opener argument to built-in open()
@@ -1090,8 +1091,6 @@ def _raw_open(self, flags, mode=0o777):
10901091
Open the file pointed by this path and return a file descriptor,
10911092
as os.open() does.
10921093
"""
1093-
if self._closed:
1094-
self._raise_closed()
10951094
return self._accessor.open(self, flags, mode)
10961095

10971096
# Public API
@@ -1125,15 +1124,11 @@ def iterdir(self):
11251124
"""Iterate over the files in this directory. Does not yield any
11261125
result for the special paths '.' and '..'.
11271126
"""
1128-
if self._closed:
1129-
self._raise_closed()
11301127
for name in self._accessor.listdir(self):
11311128
if name in {'.', '..'}:
11321129
# Yielding a path object for these makes little sense
11331130
continue
11341131
yield self._make_child_relpath(name)
1135-
if self._closed:
1136-
self._raise_closed()
11371132

11381133
def glob(self, pattern):
11391134
"""Iterate over this subtree and yield all existing files (of any
@@ -1170,8 +1165,6 @@ def absolute(self):
11701165
Use resolve() to get the canonical path to a file.
11711166
"""
11721167
# XXX untested yet!
1173-
if self._closed:
1174-
self._raise_closed()
11751168
if self.is_absolute():
11761169
return self
11771170
# FIXME this must defer to the specific flavour (and, under Windows,
@@ -1186,8 +1179,6 @@ def resolve(self, strict=False):
11861179
normalizing it (for example turning slashes into backslashes under
11871180
Windows).
11881181
"""
1189-
if self._closed:
1190-
self._raise_closed()
11911182
s = self._flavour.resolve(self, strict=strict)
11921183
if s is None:
11931184
# No symlink resolution => for consistency, raise an error if
@@ -1227,8 +1218,6 @@ def open(self, mode='r', buffering=-1, encoding=None,
12271218
Open the file pointed by this path and return a file object, as
12281219
the built-in open() function does.
12291220
"""
1230-
if self._closed:
1231-
self._raise_closed()
12321221
return io.open(self, mode, buffering, encoding, errors, newline,
12331222
opener=self._opener)
12341223

@@ -1278,8 +1267,6 @@ def touch(self, mode=0o666, exist_ok=True):
12781267
"""
12791268
Create this file with the given access mode, if it doesn't exist.
12801269
"""
1281-
if self._closed:
1282-
self._raise_closed()
12831270
if exist_ok:
12841271
# First try to bump modification time
12851272
# Implementation note: GNU touch uses the UTIME_NOW option of
@@ -1301,8 +1288,6 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
13011288
"""
13021289
Create a new directory at this given path.
13031290
"""
1304-
if self._closed:
1305-
self._raise_closed()
13061291
try:
13071292
self._accessor.mkdir(self, mode)
13081293
except FileNotFoundError:
@@ -1320,26 +1305,20 @@ def chmod(self, mode):
13201305
"""
13211306
Change the permissions of the path, like os.chmod().
13221307
"""
1323-
if self._closed:
1324-
self._raise_closed()
13251308
self._accessor.chmod(self, mode)
13261309

13271310
def lchmod(self, mode):
13281311
"""
13291312
Like chmod(), except if the path points to a symlink, the symlink's
13301313
permissions are changed, rather than its target's.
13311314
"""
1332-
if self._closed:
1333-
self._raise_closed()
13341315
self._accessor.lchmod(self, mode)
13351316

13361317
def unlink(self, missing_ok=False):
13371318
"""
13381319
Remove this file or link.
13391320
If the path is a directory, use rmdir() instead.
13401321
"""
1341-
if self._closed:
1342-
self._raise_closed()
13431322
try:
13441323
self._accessor.unlink(self)
13451324
except FileNotFoundError:
@@ -1350,34 +1329,26 @@ def rmdir(self):
13501329
"""
13511330
Remove this directory. The directory must be empty.
13521331
"""
1353-
if self._closed:
1354-
self._raise_closed()
13551332
self._accessor.rmdir(self)
13561333

13571334
def lstat(self):
13581335
"""
13591336
Like stat(), except if the path points to a symlink, the symlink's
13601337
status information is returned, rather than its target's.
13611338
"""
1362-
if self._closed:
1363-
self._raise_closed()
13641339
return self._accessor.lstat(self)
13651340

13661341
def link_to(self, target):
13671342
"""
13681343
Create a hard link pointing to a path named target.
13691344
"""
1370-
if self._closed:
1371-
self._raise_closed()
13721345
self._accessor.link_to(self, target)
13731346

13741347
def rename(self, target):
13751348
"""
13761349
Rename this path to the given path,
13771350
and return a new Path instance pointing to the given path.
13781351
"""
1379-
if self._closed:
1380-
self._raise_closed()
13811352
self._accessor.rename(self, target)
13821353
return self.__class__(target)
13831354

@@ -1387,8 +1358,6 @@ def replace(self, target):
13871358
destination if it exists, and return a new Path instance
13881359
pointing to the given path.
13891360
"""
1390-
if self._closed:
1391-
self._raise_closed()
13921361
self._accessor.replace(self, target)
13931362
return self.__class__(target)
13941363

@@ -1397,8 +1366,6 @@ def symlink_to(self, target, target_is_directory=False):
13971366
Make this path a symlink pointing to the given path.
13981367
Note the order of arguments (self, target) is the reverse of os.symlink's.
13991368
"""
1400-
if self._closed:
1401-
self._raise_closed()
14021369
self._accessor.symlink(target, self, target_is_directory)
14031370

14041371
# Convenience functions for querying the stat results

Lib/test/test_pathlib.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,13 +1720,15 @@ def test_with(self):
17201720
next(it2)
17211721
with p:
17221722
pass
1723-
# I/O operation on closed path.
1724-
self.assertRaises(ValueError, next, it)
1725-
self.assertRaises(ValueError, next, it2)
1726-
self.assertRaises(ValueError, p.open)
1727-
self.assertRaises(ValueError, p.resolve)
1728-
self.assertRaises(ValueError, p.absolute)
1729-
self.assertRaises(ValueError, p.__enter__)
1723+
# Using a path as a context manager is a no-op, thus the following
1724+
# operations should still succeed after the context manage exits.
1725+
next(it)
1726+
next(it2)
1727+
p.exists()
1728+
p.resolve()
1729+
p.absolute()
1730+
with p:
1731+
pass
17301732

17311733
def test_chmod(self):
17321734
p = self.cls(BASE) / 'fileA'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Remove undocumented support for *closing* a `pathlib.Path` object via its
2+
context manager. The context manager magic methods remain, but they are now a
3+
no-op, making `Path` objects immutable.

0 commit comments

Comments
 (0)