Two stories: the SharePoint migration layer grew from "copy items" to a resumable, verifiable site copy, and the runtime grew async/await, long-running operations and a set of write/lookup primitives that the generated surface lacked.
✨ Highlights
- Whole-site migration.
MigrationSession.add_site_task(...)resolves amigrate_alltask into one job per list - each with its own manifest and checkpoint - creates missing target containers and replays each list's structure before content is copied.include_subwebs=Truemirrors the source subsite tree. - Site-scope fidelity, not just lists. Branding, title/description, custom permission levels, role assignments, Quick Launch and top navigation, the home page, and modern plus classic pages are copied. What a client-side copy cannot produce (workflows, InfoPath forms, BCS external lists, checked-out files, minor versions) is reported instead of silently skipped. One
verify()report answers "did this web migrate?" across content, structure, settings, pages and security. - Several lists at once.
MigrationSession.start(concurrency=N)runs list jobs in parallel, each on a context clone sharing the auth context, transport andRateLimiter. Themigrate_site.pyconsole now names the list it is on, heartbeats each phase, and closes each list with copied/failures/throughput. - Async/await across the query API.
execute_query_async,execute_query_parallel_async,execute_batch_async,get_all_async,export_to_async, asyncbeforeExecuteandRateLimiter.acquire_async, with an abort path that cancels in-flight siblings. Offloaded by default; a native-asyncHttpxTransportis available via the[httpx]extra. - Long-running operations as a first-class pattern. A transport-agnostic
OperationPoller(Operation-Location / Azure-AsyncOperation /Retry-After, terminal states, continuation tokens, cancel/timeout/progress), thePollableOperationmixin,Prefer: respond-asynchelpers, and awaitableMigrationServerJob.monitor_async/progress_async/errors_async. - Streaming and large data.
BaseTransport.stream/stream_async,File.download_session(-async), bounded-memoryexport_to, resumable uploads and high-level folder downloads. - Write primitives and best-effort batches.
EntityCollection.upsert,update_all,Entity.update_properties,List.ensure_field/ensure_fields, andClientRuntimeContext.collect_batch_failures()- a rejected sub-request is recorded against its own query and the rest of the batch still runs, instead of the first failure aborting everything. - Throttling as a measured number.
ThrottleMonitorcounts responses,429/503s and theRetry-Afterseconds a run waited for (observation only - retry, backoff and pacing are untouched);--pace SECONDSis the proactive lever.
⚠️ Breaking changes
The public surface is additive; three modules moved, and the old import paths are gone. Nothing else in this release changes or removes an existing API.
Reportcalls return the newReportResult(aClientResultsubclass, so.value,bytes()and.execute_query()behave as before) instead ofClientResult[bytes].- The Security API lives at
office365.security(wasoffice365.discovery.security). TaskStatuslives atoffice365.todo.tasks.taskstatus(wasoffice365.todo.taskstatus).
🐛 Notable fixes
- Item values: hyperlink, multi-choice, multi-value lookup and person columns, taxonomy, and
ListItem.set_propertyon multi-person columns are all migrated; an empty multi-value column no longer fails verification. - Item ACLs: an empty ACL, a double inheritance break and a false mismatch are handled; fidelity writes are applied in order (permissions, authorship, timestamps) and folded into one system update, so they no longer create versions and the exact instant is restored.
- Preserved runs: authorship and timestamps no longer cost two system updates per item (three with approval state) - the principals are resolved and written once.
- Document libraries: folders are created as folders (they were uploaded as files), SharePoint's own
Forms/scaffolding and the auto-provisionedAssetsnotebook are skipped rather than reported as extra items, and Office Open XML documents are no longer flagged as content mismatches. - Record lists: content is verified instead of reported
unverifiable, writes flush and checkpoint per chunk, andconflict_resolution=OVERWRITEreaches record targets. - Warnings printed twice: structure provisioning logged each note at
warningand handed it tosession.warnings(). They are deduplicated and counted (×12 …) and the CLI reports them once. - A transport and an async token lock now follow the running event loop: a context reused from a second
asyncio.run()no longer fails withRuntimeError: Event loop is closed. SearchService.post_queryaccepts asort_list, the same argument the GET form of the query takes; the POST form had noSortListat all, so passing one failed outright.
📄 Full changelog: CHANGELOG.md · 📦 pip install --upgrade office365-rest-python-client