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

Commit 301f166

Browse filesBrowse files
[3.11] gh-93205: When rotating logs with no namer specified, match whole extension (GH-93224) (GH-115785)
(cherry picked from commit 113687a) Co-authored-by: Gabriele Catania <gabriele.ctn@gmail.com>
1 parent a27c540 commit 301f166
Copy full SHA for 301f166

File tree

Expand file treeCollapse file tree

3 files changed

+62
-19
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+62
-19
lines changed

‎Lib/logging/handlers.py

Copy file name to clipboardExpand all lines: Lib/logging/handlers.py
+24-19Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -369,32 +369,37 @@ def getFilesToDelete(self):
369369
dirName, baseName = os.path.split(self.baseFilename)
370370
fileNames = os.listdir(dirName)
371371
result = []
372-
# See bpo-44753: Don't use the extension when computing the prefix.
373-
n, e = os.path.splitext(baseName)
374-
prefix = n + '.'
375-
plen = len(prefix)
376-
for fileName in fileNames:
377-
if self.namer is None:
378-
# Our files will always start with baseName
379-
if not fileName.startswith(baseName):
380-
continue
381-
else:
372+
if self.namer is None:
373+
prefix = baseName + '.'
374+
plen = len(prefix)
375+
for fileName in fileNames:
376+
if fileName[:plen] == prefix:
377+
suffix = fileName[plen:]
378+
if self.extMatch.match(suffix):
379+
result.append(os.path.join(dirName, fileName))
380+
else:
381+
# See bpo-44753: Don't use the extension when computing the prefix.
382+
n, e = os.path.splitext(baseName)
383+
prefix = n + '.'
384+
plen = len(prefix)
385+
for fileName in fileNames:
382386
# Our files could be just about anything after custom naming, but
383387
# likely candidates are of the form
384388
# foo.log.DATETIME_SUFFIX or foo.DATETIME_SUFFIX.log
385389
if (not fileName.startswith(baseName) and fileName.endswith(e) and
386390
len(fileName) > (plen + 1) and not fileName[plen+1].isdigit()):
387391
continue
388392

389-
if fileName[:plen] == prefix:
390-
suffix = fileName[plen:]
391-
# See bpo-45628: The date/time suffix could be anywhere in the
392-
# filename
393-
parts = suffix.split('.')
394-
for part in parts:
395-
if self.extMatch.match(part):
396-
result.append(os.path.join(dirName, fileName))
397-
break
393+
if fileName[:plen] == prefix:
394+
suffix = fileName[plen:]
395+
# See bpo-45628: The date/time suffix could be anywhere in the
396+
# filename
397+
398+
parts = suffix.split('.')
399+
for part in parts:
400+
if self.extMatch.match(part):
401+
result.append(os.path.join(dirName, fileName))
402+
break
398403
if len(result) < self.backupCount:
399404
result = []
400405
else:

‎Lib/test/test_logging.py

Copy file name to clipboardExpand all lines: Lib/test/test_logging.py
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5767,6 +5767,43 @@ def test_compute_files_to_delete(self):
57675767
self.assertTrue(fn.startswith(prefix + '.') and
57685768
fn[len(prefix) + 2].isdigit())
57695769

5770+
def test_compute_files_to_delete_same_filename_different_extensions(self):
5771+
# See GH-93205 for background
5772+
wd = pathlib.Path(tempfile.mkdtemp(prefix='test_logging_'))
5773+
self.addCleanup(shutil.rmtree, wd)
5774+
times = []
5775+
dt = datetime.datetime.now()
5776+
n_files = 10
5777+
for _ in range(n_files):
5778+
times.append(dt.strftime('%Y-%m-%d_%H-%M-%S'))
5779+
dt += datetime.timedelta(seconds=5)
5780+
prefixes = ('a.log', 'a.log.b')
5781+
files = []
5782+
rotators = []
5783+
for i, prefix in enumerate(prefixes):
5784+
backupCount = i+1
5785+
rotator = logging.handlers.TimedRotatingFileHandler(wd / prefix, when='s',
5786+
interval=5,
5787+
backupCount=backupCount,
5788+
delay=True)
5789+
rotators.append(rotator)
5790+
for t in times:
5791+
files.append('%s.%s' % (prefix, t))
5792+
# Create empty files
5793+
for f in files:
5794+
(wd / f).touch()
5795+
# Now the checks that only the correct files are offered up for deletion
5796+
for i, prefix in enumerate(prefixes):
5797+
backupCount = i+1
5798+
rotator = rotators[i]
5799+
candidates = rotator.getFilesToDelete()
5800+
self.assertEqual(len(candidates), n_files - backupCount)
5801+
matcher = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$")
5802+
for c in candidates:
5803+
d, fn = os.path.split(c)
5804+
self.assertTrue(fn.startswith(prefix))
5805+
suffix = fn[(len(prefix)+1):]
5806+
self.assertRegex(suffix, matcher)
57705807

57715808
def secs(**kw):
57725809
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in :class:`logging.handlers.TimedRotatingFileHandler` where multiple rotating handler instances pointing to files with the same name but different extensions would conflict and not delete the correct files.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.