Skip to content

Commit f2f5e1c

Browse files
committed
Ignore PermissionError during import from cwd
On macOS `getcwd(3)` can return EACCES if a path component isn't readable, resulting in PermissionError. `PathFinder.find_spec()` now catches these and ignores them - the same treatment as a missing/deleted cwd.
1 parent 9c15202 commit f2f5e1c

File tree

5 files changed

+39
-5
lines changed

5 files changed

+39
-5
lines changed

Doc/reference/import.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -871,10 +871,10 @@ module.
871871

872872
The current working directory -- denoted by an empty string -- is handled
873873
slightly differently from other entries on :data:`sys.path`. First, if the
874-
current working directory is found to not exist, no value is stored in
875-
:data:`sys.path_importer_cache`. Second, the value for the current working
876-
directory is looked up fresh for each module lookup. Third, the path used for
877-
:data:`sys.path_importer_cache` and returned by
874+
current working directory cannot be determined or is found to not exist, no
875+
value is stored in :data:`sys.path_importer_cache`. Second, the value for the
876+
current working directory is looked up fresh for each module lookup. Third,
877+
the path used for :data:`sys.path_importer_cache` and returned by
878878
:meth:`importlib.machinery.PathFinder.find_spec` will be the actual current
879879
working directory and not the empty string.
880880

Lib/importlib/_bootstrap_external.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,7 @@ def _path_importer_cache(cls, path):
15031503
if path == '':
15041504
try:
15051505
path = _os.getcwd()
1506-
except FileNotFoundError:
1506+
except (FileNotFoundError, PermissionError):
15071507
# Don't cache the failure as the cwd can easily change to
15081508
# a valid directory later on.
15091509
return None

Lib/test/test_importlib/import_/test_path.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from test.support import os_helper
2+
from test.test_importlib import fixtures
13
from test.test_importlib import util
24

35
importlib = util.import_importlib('importlib')
@@ -153,6 +155,25 @@ def test_deleted_cwd(self):
153155
# Do not want FileNotFoundError raised.
154156
self.assertIsNone(self.machinery.PathFinder.find_spec('whatever'))
155157

158+
@os_helper.skip_unless_working_chmod
159+
def test_permission_error_cwd(self):
160+
# gh-115911
161+
with (
162+
fixtures.tempdir() as new_dir,
163+
fixtures.save_mode(new_dir),
164+
fixtures.save_cwd(),
165+
util.import_state(path=['']),
166+
):
167+
os.chdir(new_dir)
168+
try:
169+
os.chmod(new_dir, 0o000)
170+
except OSError:
171+
self.skipTest("platform does not allow "
172+
"changing mode of the cwd")
173+
174+
# Do not want PermissionError raised.
175+
self.assertIsNone(self.machinery.PathFinder.find_spec('whatever'))
176+
156177
def test_invalidate_caches_finders(self):
157178
# Finders with an invalidate_caches() method have it called.
158179
class FakeFinder:

Lib/test/test_importlib/metadata/fixtures.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ def save_cwd():
4444
os.chdir(orig)
4545

4646

47+
@contextlib.contextmanager
48+
def save_mode(path, *, follow_symlinks=True):
49+
path = pathlib.Path(path)
50+
orig = path.stat(follow_symlinks=follow_symlinks)
51+
try:
52+
yield
53+
finally:
54+
path.chmod(orig.st_mode, follow_symlinks=follow_symlinks)
55+
56+
4757
@contextlib.contextmanager
4858
def tempdir_as_cwd():
4959
with tempdir() as tmp:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
If the current working directory cannot be determined due to permissions,
2+
then import will no longer raise :exc:`PermissionError`. Patch by Alex
3+
Willmer.

0 commit comments

Comments
 (0)