Skip to content

Commit

Permalink
Ignore PermissionError during import from cwd
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
moreati committed Jun 20, 2024
1 parent 55596ae commit 2c05daa
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 5 deletions.
8 changes: 4 additions & 4 deletions Doc/reference/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -871,10 +871,10 @@ module.

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

Expand Down
2 changes: 1 addition & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -1506,7 +1506,7 @@ def _path_importer_cache(cls, path):
if path == '':
try:
path = _os.getcwd()
except FileNotFoundError:
except (FileNotFoundError, PermissionError):
# Don't cache the failure as the cwd can easily change to
# a valid directory later on.
return None
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_importlib/import_/test_path.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from test.support import os_helper
from test.test_importlib import util
from test.test_importlib.metadata import fixtures

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

@os_helper.skip_unless_working_chmod
def test_permission_error_cwd(self):
# gh-115911
with (
fixtures.tempdir() as new_dir,
fixtures.save_mode(new_dir),
fixtures.save_cwd(),
util.import_state(path=['']),
):
os.chdir(new_dir)
try:
os.chmod(new_dir, 0o000)
except OSError:
self.skipTest("platform does not allow "
"changing mode of the cwd")

# Do not want PermissionError raised.
self.assertIsNone(self.machinery.PathFinder.find_spec('whatever'))

def test_invalidate_caches_finders(self):
# Finders with an invalidate_caches() method have it called.
class FakeFinder:
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_importlib/metadata/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ def save_cwd():
os.chdir(orig)


@contextlib.contextmanager
def save_mode(path, *, follow_symlinks=True):
path = pathlib.Path(path)
orig = path.stat(follow_symlinks=follow_symlinks)
try:
yield
finally:
path.chmod(orig.st_mode, follow_symlinks=follow_symlinks)


@contextlib.contextmanager
def tempdir_as_cwd():
with tempdir() as tmp:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
If the current working directory cannot be determined due to permissions,
then import will no longer raise :exc:`PermissionError`. Patch by Alex
Willmer.

0 comments on commit 2c05daa

Please sign in to comment.