Skip to content
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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-85253: Exclude __pycache__ directories from backups using CACHEDIR.TAG #21060

Closed
wants to merge 8 commits into from
21 changes: 21 additions & 0 deletions Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,7 @@ def set_data(self, path, data, *, _mode=0o666):
parent, part = _path_split(parent)
path_parts.append(part)
# Create needed directories.
just_created_cache_dir = False
for part in reversed(path_parts):
parent = _path_join(parent, part)
try:
Expand All @@ -1247,6 +1248,11 @@ def set_data(self, path, data, *, _mode=0o666):
_bootstrap._verbose_message('could not create {!r}: {!r}',
parent, exc)
return
else:
just_created_cache_dir = True
# To avoid polluting backups with __pycache__ directories and .pyc files
if just_created_cache_dir:
_exclude_directory_from_backups(parent)
try:
_write_atomic(path, data, _mode)
_bootstrap._verbose_message('created {!r}', path)
Expand All @@ -1256,6 +1262,21 @@ def set_data(self, path, data, *, _mode=0o666):
exc)


def _exclude_directory_from_backups(directory):
"""Exclude the directory from backups using a CACHEDIR.TAG file.

If the file exists or there's a permission error this is a no-op.
"""
try:
with _io.FileIO(_path_join(directory, 'CACHEDIR.TAG'), 'x') as f:
merwok marked this conversation as resolved.
Show resolved Hide resolved
f.write(b"""Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by CPython.
# For information about cache directory tags, see: https://bford.info/cachedir/
""")
except (FileExistsError, PermissionError):
pass


class SourcelessFileLoader(FileLoader, _LoaderBasics):

"""Loader which handles sourceless file imports."""
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,21 @@ def test_import_pyc_path(self):
'bytecode file {!r} for {!r} does not '
'exist'.format(pyc_path, TESTFN))

@skip_if_dont_write_bytecode
def test_cachedir_tag_exists(self):
self.assertFalse(os.path.exists('__pycache__'))
__import__(TESTFN)
self.assertTrue(os.path.exists('__pycache__'))
cachedir_tag = os.path.join('__pycache__', 'CACHEDIR.TAG')
self.assertTrue(os.path.exists(cachedir_tag),
f'CACHEDIR.TAG file {cachedir_tag} for {TESTFN} does not exist')
with open(cachedir_tag) as f:
cachedir_tag_content = f.read()
self.assertTrue(
cachedir_tag_content.startswith('Signature: 8a477f597d28d172789f06886806bc55'),
f'CACHEDIR.TAG file {cachedir_tag} for {TESTFN} does not contain the right signature',
)

@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
@skip_if_dont_write_bytecode
Expand Down
Loading