Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-115911: Ignore PermissionError during import from cwd #116131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
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
Next Next commit
Ignore PermissionError when checking cwd during import
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.

Introduces `test.support.os_helper.save_mode(path, ...)`, a context manager
that restores the mode of a path on exit.

This is allows finer control of exception handling and robust environment
restoration across platforms in `FinderTests.test_permission_error_cwd()`.

Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
Co-authored-by: Brett Cannon <brett@python.org>
  • Loading branch information
3 people committed Nov 5, 2024
commit cf4a38ade817aa5e82e9551609a3fbfdc1a620ee
8 changes: 4 additions & 4 deletions 8 Doc/reference/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -762,10 +762,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 not to 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 2 Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,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
27 changes: 27 additions & 0 deletions 27 Lib/test/support/os_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,33 @@ def skip_unless_working_chmod(test):
return test if ok else unittest.skip(msg)(test)


@contextlib.contextmanager
def save_mode(path, *, quiet=False):
"""Context manager that restores the mode (permissions) of *path* on exit.

Arguments:

path: Path of the file to restore the mode of.

quiet: if False (the default), the context manager raises an exception
on error. Otherwise, it issues only a warning and keeps the current
working directory the same.

"""
saved_mode = os.stat(path)
try:
yield
finally:
try:
os.chmod(path, saved_mode.st_mode)
except OSError as exc:
if not quiet:
raise
warnings.warn(f'tests may fail, unable to restore the mode of '
f'{path!r} to {saved_mode.st_mode}: {exc}',
RuntimeWarning, stacklevel=3)


# Check whether the current effective user has the capability to override
# DAC (discretionary access control). Typically user root is able to
# bypass file read, write, and execute permission checks. The capability
Expand Down
23 changes: 23 additions & 0 deletions 23 Lib/test/test_importlib/import_/test_path.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from test.support import os_helper
from test.test_importlib import util

importlib = util.import_importlib('importlib')
Expand Down Expand Up @@ -153,6 +154,28 @@ 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: Test that an unreadable CWD does not break imports, in
# particular during early stages of interpreter startup.
with (
os_helper.temp_dir() as new_dir,
os_helper.save_mode(new_dir),
os_helper.change_cwd(new_dir),
util.import_state(path=['']),
):
# chmod() is done here (inside the 'with' block) because the order
# of teardown operations cannot be the reverse of setup order. See
# https://github.com/python/cpython/pull/116131#discussion_r1739649390
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
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.
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.