Skip to content

Delete (or recycle) file versions.

Works for large files too (2 GB+): versions are enumerated with a minimal property set and each deletion runs through execute_query_with_incremental_retry so slow, storage-heavy operations can't hang and transient failures are retried.

Examples: python delete.py --file-url "Shared Documents/report.docx" # keep newest 1 python delete.py --file-url "Shared Documents/big.mp4" --keep 5 # keep newest 5 python delete.py --file-url "Shared Documents/big.mp4" --all # delete every non-current version python delete.py --file-url "Shared Documents/big.mp4" --recycle # recycle instead of hard-delete

https://learn.microsoft.com/en-us/graph/api/... (SharePoint versioning REST)

View source

import argparse

from office365.sharepoint.client_context import ClientContext
from tests.settings import client_id, password, site_url, tenant, username

DEFAULT_KEEP = 1


def main():
    parser = argparse.ArgumentParser(description="Delete file versions")
    parser.add_argument("--file-url", required=True, help="server-relative file URL")
    parser.add_argument(
        "--keep",
        type=int,
        default=DEFAULT_KEEP,
        help=f"newest versions to keep (default: {DEFAULT_KEEP})",
    )
    parser.add_argument("--all", action="store_true", help="delete every version except the current one")
    parser.add_argument("--recycle", action="store_true", help="recycle versions instead of deleting them permanently")
    parser.add_argument("--timeout", type=int, default=30, help="request timeout in seconds (default: 30)")
    args = parser.parse_args()

    keep = 0 if args.all else args.keep
    if keep < 0:
        parser.error("--keep must be >= 0")

    ctx = (
        ClientContext(site_url)
        .with_username_and_password(tenant=tenant, client_id=client_id, username=username, password=password)
        .with_transport(timeout=args.timeout)
    )

    versions_col = ctx.web.get_file_by_server_relative_path(args.file_url).versions
    ctx.load(versions_col, ["ID", "VersionLabel", "IsCurrentVersion"]).execute_query()

    ordered = sorted(versions_col, key=lambda v: v.id or 0, reverse=True)
    to_remove = [v for v in ordered[keep:] if not v.is_current_version]

    print(f"{len(versions_col)} version(s), keeping {keep}; removing {len(to_remove)}:")
    for version in to_remove:
        assert version.id is not None
        if args.recycle:
            versions_col.recycle_by_id(version.id)
        else:
            versions_col.delete_by_id(version.id)
        ctx.execute_query_with_incremental_retry(max_retry=3)
        print(f"  {'recycled' if args.recycle else 'deleted'} {version.version_label or '?'}")


if __name__ == "__main__":
    main()

← Back to Versions