* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
<whatsnew315-pep782>`
* :ref:`Improved error messages <whatsnew315-improved-error-messages>`
+* :ref:`__pycache__ directories now contain a .gitignore file
+ <whatsnew315-pycache-gitignore>`
New features
for any class.
(Contributed by Serhiy Storchaka in :gh:`41779`.)
+.. _whatsnew315-pycache-gitignore:
+
+* :file:`__pycache__` directories now contain a :file:`.gitignore` file for Git
+ that ignores their contents.
+ (Contributed by Stan Ulbrych in :gh:`141081`.)
+
New modules
===========
_bootstrap._verbose_message('could not create {!r}: {!r}',
parent, exc)
return
+
+ if part == _PYCACHE:
+ gitignore = _path_join(parent, '.gitignore')
+ try:
+ _path_stat(gitignore)
+ except FileNotFoundError:
+ gitignore_content = b'# Created by CPython\n*\n'
+ try:
+ _write_atomic(gitignore, gitignore_content, _mode)
+ except OSError:
+ pass
+ except OSError:
+ pass
try:
_write_atomic(path, data, _mode)
_bootstrap._verbose_message('created {!r}', path)
dirname = os.path.dirname(cfile)
if dirname:
os.makedirs(dirname)
+ if os.path.basename(dirname) == '__pycache__':
+ gitignore = os.path.join(dirname, '.gitignore')
+ if not os.path.exists(gitignore):
+ try:
+ with open(gitignore, 'wb') as f:
+ f.write(b'# Created by CPython\n*\n')
+ except OSError:
+ pass
except FileExistsError:
pass
if invalidation_mode == PycInvalidationMode.TIMESTAMP:
['-m', 'compileall', '-q', self.pkgdir]))
# Verify the __pycache__ directory contents.
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
- expected = sorted(base.format(sys.implementation.cache_tag, ext)
- for base in ('__init__.{}.{}', 'bar.{}.{}'))
+ expected = ['.gitignore'] + sorted(
+ base.format(sys.implementation.cache_tag, ext)
+ for base in ('__init__.{}.{}', 'bar.{}.{}')
+ )
self.assertEqual(sorted(os.listdir(self.pkgdir_cachedir)), expected)
# Make sure there are no .pyc files in the source directory.
self.assertFalse([fn for fn in os.listdir(self.pkgdir)
data[8:16],
)
+ @util.writes_bytecode_files
+ def test_gitignore_in_pycache(self):
+ with util.create_modules('_temp') as mapping:
+ source = mapping['_temp']
+ loader = self.machinery.SourceFileLoader('_temp', source)
+ mod = types.ModuleType('_temp')
+ mod.__spec__ = self.util.spec_from_loader('_temp', loader)
+ loader.exec_module(mod)
+ pyc = os.path.dirname(self.util.cache_from_source(source))
+ gitignore = os.path.join(pyc, '.gitignore')
+ self.assertTrue(os.path.exists(gitignore))
+ with open(gitignore, 'rb') as f:
+ t = f.read()
+ self.assertEqual(t, b'# Created by CPython\n*\n')
+
(Frozen_SimpleTest,
Source_SimpleTest
with self.assertRaises(py_compile.PyCompileError):
py_compile.compile(bad_coding, doraise=True, quiet=1)
+ def test_gitignore_created(self):
+ py_compile.compile(self.source_path)
+ self.assertTrue(os.path.exists(self.cache_path))
+ pyc = os.path.dirname(self.cache_path)
+ gitignore = os.path.join(pyc, '.gitignore')
+ self.assertTrue(os.path.exists(gitignore))
+ with open(gitignore, 'rb') as f:
+ text = f.read()
+ self.assertEqual(text, b'# Created by CPython\n*\n')
+
class PyCompileTestsWithSourceEpoch(PyCompileTestsBase,
unittest.TestCase,
--- /dev/null
+When ``__pycache__`` directories are created, they now contain a
+``.gitignore`` file that ignores their contents.