OneDrive & SharePoint files — a guided tour¶
A drive is the file layer of Microsoft 365: every user has one
(client.me.drive), every site has one, and both speak the same
driveItem API.
This page is a tour — read it top to bottom and you'll know the model, the
common tasks, and where the hard cases live.
This client targets the Graph endpoint (
graph.microsoft.com). Two things from the Microsoft docs you'll meet but not here: CORS / JavaScript (this is a server-side client) andX-HTTP-Methodverb tunneling (Graph uses the real verb, e.g.PATCH).
The model in one picture¶
flowchart LR
U["User / Site"] --> D[Drive]
D --> R["root<br/>(a DriveItem)"]
R --> C["children<br/>folders & files"]
C --> V["versions"]
C --> P["permissions<br/>+ sharing links"]
C --> X["content<br/>(bytes)"]
D --> S["special<br/>Documents · approot · …"]
D --> L["list → items<br/>custom columns / metadata"]
Everything is a DriveItem addressed from a Drive; folders and files
are the same type (a folder or file facet tells them apart), and custom
metadata rides on the item's backing listItem.
Start here (5 minutes)¶
quickstart.py walks the five operations you'll do most, end
to end, and cleans up after itself:
sequenceDiagram
participant You as quickstart.py
participant G as Microsoft Graph
You->>G: upload root:/quickstart.txt
You->>G: GET /root/children (list)
You->>G: GET /items/…/content (read back)
You->>G: POST /items/…/copy (202 + monitor URL)
G-->>You: poll until completed (long-running)
You->>G: DELETE items + folder
Addressing items¶
The client addresses entities by natural keys, not just opaque ids: people by user principal name, sites by path or URL, files and folders by path, lists by title. Names are case-preserving but matched case-insensitively (the OneDrive rule); ids are stable, so keep one around when you need a durable reference.
drive = client.me.drive
drive.root # the drive root
drive.root.get_by_path("Reports/2024/Q1.xlsx") # a file/folder by path
drive.items[item_id] # by id (stable, shareable)
drive.special["Documents"] # a special folder by name
Reaching another owner's drive follows the same idea — no ids required:
client.users["ada@contoso.com"].drive # a user's drive, by UPN
client.sites.get_by_path("/sites/marketing").drive # a site's library, by path
client.sites.get_by_url("https://contoso.sharepoint.com/sites/marketing").drive # by URL
| Want | Reach for | Example |
|---|---|---|
| Follow a path | drive.root.get_by_path |
files/addressing.py |
| Address by id | drive.items[id] |
files/manage.py |
| A special / app folder | drive.special |
appfolder/app_folder.py |
| Another user's drive | client.users["upn"].drive |
drives/user_drive.py |
| A site's drive | client.sites.get_by_path(...).drive |
lists/library.py |
Working with files¶
Files are DriveItems with a file facet. Upload small files in one call;
use a resumable session for large ones. Copy and move are metadata operations
(asynchronous for copy), so they're fast even for big files.
| Operation | Example |
|---|---|
| Upload & download (round-trip) | files/upload_download.py |
| Upload a large file (resumable session) | patterns/large-data/upload_large.py |
| Ensure a file exists (idempotent) | files/ensure_file.py |
| Copy, rename, move, versions, delete | files/manage.py |
| Copy a large file and await it | patterns/lro/copy_and_wait.py |
| Check out, edit, check in | files/lifecycle.py |
| Tag a file with custom metadata | files/custom_metadata.py |
| Read structured data back (JSON) | files/download_json.py |
→ Guide: files/README.md
Working with folders¶
A folder is a DriveItem with a folder facet and a children collection.
Walk it, page it, download it as a zip, or upload a whole local tree.
| Operation | Example |
|---|---|
| Create & navigate folders | folders/manage.py |
| Build a nested structure | folders/create_nested.py |
| List everything, recursively | folders/list_recursive.py |
| Upload a local folder tree | folders/upload_folder.py |
| Download a folder (zip) / in parallel | folders/download_folder.py, patterns/large-data/download_parallel.py |
| Rename, move, delete | folders/move_delete.py |
→ Guide: folders/README.md
Working with drives¶
A drive holds the items and reports quota/storage. Query it across the tenant for governance and reporting.
| Operation | Example |
|---|---|
| Recent, shared-with-me, followed | drives/explore.py |
| Search a keyword across all drives | ../patterns/discovery/search_across_drives.py |
| Tenant-wide storage report | drives/storage_report.py |
| A user's drive and quota | drives/user_drive.py |
→ Guide: drives/README.md
Working with SharePoint sites¶
A site is a container of drives, lists, pages and permissions. Get it by URL or path, then read or grant access.
| Operation | Example |
|---|---|
| Get the root / a site by path | sites/get_site.py, sites/get_site_by_path.py |
| List all sites / subsites | sites/list_all.py, sites/subsites.py |
| Who has access; grant & revoke | sites/site_permissions.py, sites/grant_permission.py |
| Add / remove an owner | sites/add_admin.py, sites/remove_admin.py |
| Sharing link / external-sharing audit | sites/share_site_link.py, sites/external_sharing.py |
| Analytics, activity, stale sites, CSV export | sites/analytics_report.py, sites/find_stale.py, sites/export_csv.py |
→ Guide: sites/README.md
Working with document libraries (lists)¶
A document library is a list that also acts as a drive. Reach items as
DriveItems or as list items with custom columns.
| Operation | Example |
|---|---|
| Create a library / list with columns | lists/create_list.py |
| Manage items (CRUD) & columns | lists/manage_items.py, lists/columns.py |
| Query items (filter/select/order) | lists/query_items.py |
| Library as a drive | lists/library.py |
| Bulk import / export | lists/import_export.py |
→ Guide: lists/README.md · also OData query parameters
Working with site pages¶
Modern pages: create, lay out with web parts, and drive the check-in/publish workflow.
| Operation | Example |
|---|---|
| Page lifecycle (create/update/publish/delete) | pages/manage.py |
| List pages with publishing state | pages/list_pages.py |
| Check in & publish | pages/publish_flow.py |
| Inspect web parts | pages/webparts.py |
→ Guide: pages/README.md
Working with the term store¶
The managed taxonomy: groups → sets → terms. Import, export, search and clean it up.
| Operation | Example |
|---|---|
| Create groups, sets, terms | termstore/create_terms.py |
| Search by term label | termstore/search_term.py |
| Export the hierarchy | termstore/export_store.py |
| Import from JSON | termstore/import_store.py |
→ Guide: termstore/README.md
Working with Excel workbooks¶
An .xlsx in a drive is a workbook you can drive server-side — sessions, tables,
ranges and formulas — without downloading it.
| Operation | Example |
|---|---|
| Read tables & data | excel/read_table.py |
| Workbook sessions | excel/workbook_sessions.py |
| Tables, worksheets, ranges | excel/tables.py, excel/worksheets.py, excel/ranges.py |
→ Guide: excel/README.md
Working with change notifications¶
Instead of polling a drive, have Graph push an HTTPS notification when something changes.
| Operation | Example |
|---|---|
| Subscribe, renew, remove | webhooks/subscribe_to_drive.py |
| Reconcile after downtime (delta) | ../patterns/discovery/delta_sync.py |
→ Guide: webhooks/README.md
Going further (patterns)¶
Cross-cutting and advanced scenarios live in ../patterns/ so
they're reusable across products:
| When you need | Pattern |
|---|---|
Copy/clone/export that returns 202 Accepted |
patterns/lro/ · monitor CLI |
| Concurrency, async twins, batching | patterns/async/ |
| Resumable uploads, parallel downloads, exports | patterns/large-data/ |
$select / $filter / $orderby / $expand |
patterns/odata-query/ |
| Errors, throttling, retries | patterns/reliability/ |
Permissions & least privilege¶
Pick the narrowest scope that works. Examples use the delegated
username/password flow from tests.settings; the same builders accept any
credential (see the auth/ examples).
| Permission | Grants |
|---|---|
Files.Read |
Read, search, delta, analytics |
Files.ReadWrite |
Upload, download, copy, move, share — the common default |
Files.ReadWrite.All |
Above, plus any user's drive and the recycle bin |
Files.ReadWrite.AppFolder |
Only the app's private approot folder |
Sites.Read[Write].All |
Sites, lists, pages, term store |
Analytics.Read |
File activity stats |
Recommendations & gotchas¶
- Copy is long-running. A
copy()answers202; await it (or poll the monitor URL). Never fire-and-forget — and never batch it (the monitor URL is lost). →patterns/lro/lro_in_batch_guard.py - Address by natural key (UPN, path, URL, title) for readable code; names are
case-preserving but matched case-insensitively. Keep an id only when you need a
durable reference. →
files/addressing.py parentReferencebefore copy/move: load the destination (dest.get().execute_query()) so the payload carries the drive id.- Delta tokens are opaque — persist and replay the returned link.
- Throttling is normal: honor
Retry-After(the LRO poller does it for you). - Upload sessions expire; large uploads should resume, not restart.