Skip to content

[Code scan] Preserve older backups in file_safe_backup #7540

Description

@njzjz

This issue is a result of a Codex global repository scan.

file_safe_backup() is documented to rotate existing backups so that the latest file becomes *.bak.0 and older backups move to higher indices. The current rename loop computes j = len(fbak) - i + 1, then replaces f'.{j}' in the filename. For existing STRU.bak.0 and STRU.bak.1, this starts at j == 3, so no filename changes. The subsequent rename of the live file to STRU.bak.0 can overwrite the previous backup instead of preserving it.

def file_safe_backup(fn: Path, suffix: str = 'bak'):
'''for the case where there are already files with the same name,
add a suffix to the file name, like `STRU.bak.0`. If there are
already `STRU.bak.0`, rename the elder to `STRU.bak.1` and let
the latest one be `STRU.bak.0`.
Parameters
----------
fn : Path
The path to the file to backup. Note: it must be provided
as the Path object so that its folder is accessible by this
function.
suffix : str, optional
The suffix to add to the file name. Default is 'bak'
'''
assert isinstance(fn, Path)
where = fn.parent
# get the backup files
fbak = sorted(list(where.glob(f'{fn.name}.{suffix}.*')),
key=lambda p: int(p.name.split('.')[-1]))
if fbak:
# rename the elder by adding 1 to the suffix
for i, f in enumerate(fbak[::-1]): # reverse order, to avoid overwrite
j = len(fbak) - i + 1 #: STRU.bak.i -> STRU.bak.i+1
fname = f.name.replace(f'.{j}', f'.{j+1}')
f.rename(f.parent / fname)
# backup the latest file, if there is one
if fn.exists():
fn.rename(fn.parent / f'{fn.name}.{suffix}.0')

Relevant code:

fbak = sorted(list(where.glob(f'{fn.name}.{suffix}.*')),
              key=lambda p: int(p.name.split('.')[-1]))
if fbak:
    for i, f in enumerate(fbak[::-1]):
        j = len(fbak) - i + 1 #: STRU.bak.i -> STRU.bak.i+1
        fname = f.name.replace(f'.{j}', f'.{j+1}')
        f.rename(f.parent / fname)

if fn.exists():
    fn.rename(fn.parent / f'{fn.name}.{suffix}.0')

Suggested fix:

Parse each backup index from the filename and rename in descending index order, e.g. old_idx -> old_idx + 1, before moving the current file to index 0. Also consider ignoring or handling non-integer backup suffixes explicitly.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions