NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1124 most downloaded on PyPI
Microsoft 365 & Microsoft Graph Library for Python
Last release 7 days ago
27 Sep 2026
Ships unpredictably
gaps range from 2 weeks to 1.2 years
Nearly every release is documented
notes for 54 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
61 releases · first in 2017
A large release centered on data import/export , large-list reliability , and the SharePoint Migration API .
A large release centered on data import/export, large-list reliability, and the SharePoint Migration API.
ViewXml in Python instead of raw strings: Caml.text("Status").eq("Active"), Caml.lookup("Category").id().in_([2, 3]), binary-nested .and_()/.or_()/.not_(), plus where / order_by / group_by / view_fields.ImportResult queues, executes and discards chunks (bounded memory) with checkpoints (resume interrupted runs), live progress, dead-lettering, and keyed skip/upsert so re-runs never duplicate rows.office365.migration.package (manifest/export XML, PackageBuilder, AES-256-CBC staging, SharePointPackageTarget) and SPMT-style MigrationSession / MigrationServerJob with server-side provisioning and progress. Document-library subset.FileLockedException (.lock_owner, .GUIDANCE) and SPFileCheckOutException, opt-in retry with retry_on(FileLockedException), and bypass_shared_lock=True.Folder.get_files and List.get_items(page_size=...), List.ensure_indexed() (the real fix for #427), typed SPQueryThrottledException, and a one-time warning at the 5,000-item threshold. See docs/large-lists.md.SPFileCheckOutException, SPListDataValidationException, …).DriveItem.ensure_file(), the shared get_or_create helper, and ensure_*(on_conflict="skip"|"update") across fields, lists and content types.from_* is now the streaming entry (returns ImportResult); queue_* is the deferred queue-all path. Removed import_* and to_json_file / from_json_file (use export_to(..., format="json") / from_json); FieldCollection.from_dataframe → ensure_from_dataframe.List.from_dataframe() returns an ImportResult driver (was List); progress is keyword-only.MigrationOptions.preserve_timestamps now defaults to False, and requesting fidelity an adapter pair can't honor raises NotImplementedError instead of silently doing nothing.ensure_* helpers now return the ensured entity/entities.allow_ntlm=True fixed.User.remove_by_id).$skip paging no longer collides with a server $skiptoken.CreateMigrationJobEncrypted sends the key as base64.Name → FileLeafRef).📄 Full changelog: CHANGELOG.md · 📦 pip install --upgrade office365-rest-python-client
One column per quarter.
FileLockedException (also exported as office365.sharepoint.exceptions.SPFileLockedException)
classifies the Microsoft.SharePoint.SPFileLockException /
Graph resourceLocked shared-lock response and parses the lock holder into
.lock_owner, with remediation in .GUIDANCE. execute_query_retry(..., is_retriable=retry_on(FileLockedException)) opts into retrying otherwise
permanent lock errors (new retry_on helper), and delete_object( bypass_shared_lock=True) sends Prefer: bypass-shared-lock on Graph and
SharePoint. See examples/sharepoint/files/handle_locked_file.py.DriveItem.ensure_file(path, content, on_conflict=...) — the file counterpart of ensure_folder. Missing parent
folders are created, then the leaf file is reused when present ("skip", the
default, never re-sends content) or overwritten in a single request
("replace"). Fully deferred on one execute_query(), backed by the shared
get_or_create helper.Caml / CamlQuery.builder() construct ViewXml
from composable Python expressions instead of raw CAML strings — fluent
comparisons (Caml.text("Status").eq("Active")), field-typed helpers
(Caml.lookup("Category").id().in_([2, 3])), value nodes (Caml.now), logical
joins (.and_()/.or_()/.not_(), &/|/~, variadic Caml.and_/or_) that
always render binary-nested CAML, plus
where/order_by/group_by/row_limit/scope/view_fields. Raw ViewXml/parse
remain supported. New modules under office365/sharepoint/listitems/caml/
(values, fields, expressions, builder); removed the unused types stubs.ImportResult — a deferred, source-agnostic streaming import driver. Chunks
are queued, executed, and discarded (bounded memory), and the caller picks the
terminal: execute_query() (sequential), execute_batch(...) (server-side,
concurrent), or iterating the driver. ClientObjectCollection.import_records()
streams record batches; List.from_dataframe() streams a DataFrame / CSV
source and provisions the columns once.List.import_dataframe(..., key=..., key_field="MigrationKey", on_conflict="skip"|"upsert") derives a SHA-256 key
from the given natural-key column(s), stores it in a dedicated field, loads the
existing keys once, and skips or updates already-present rows — so a re-run
never duplicates. Backed by runtime.converters.upsert.keyed_queue +
ListItemUpsertTarget and a conflict-resolution queue hook on ImportResult
(ImportStats.skipped); enforce_unique=True and dry_run=True are supported.ImportResult accepts a checkpoint — a path
(FileCheckpointStore), an ImportCheckpoint/None (MemoryCheckpointStore),
or any CheckpointStore — and persists the committed cursor after each chunk
(atomically), so an interrupted long-running run resumes by skipping the
already-committed chunks. ImportResult.resumed_from/.checkpoint and
ImportStats.resumed_from (in summary()) expose the resumed offset.
on_error="collect" records a failing chunk (ImportStats.errors +
checkpoint.failures) and continues instead of aborting.ClientObjectCollection.clear() and a concurrency argument on
Entity.execute_batch().OperationStats — a shared counter base for bulk operations — with
ImportStats and MigrationStats as specializations (no lossy conversion
between them).progress hook now fires immediately (with the
resumed offset, so a bar appears with its total) and then per committed chunk
and per completed batch during execute_batch, instead of only once per
queued chunk. run_parallel reports progress on the calling thread as tasks
complete (the completed result in Progress.items), and execute_batch's
success_callback now fires per batch in sequential mode too (it previously
never fired there). List.import_from(..., total=...) lets callers supply the
known total so the progress percentage/ETA is meaningful.preserve_timestamps / preserve_permissions client-side when the adapters
support it, via new optional hooks DataSource.read_permissions(item) /
DataTarget.apply_timestamps(item) / DataTarget.apply_permissions(item, permissions) (with PermissionEntry). The SharePoint library adapters restore
Created/Modified through ValidateUpdateListItem and recreate the source
role assignments (same-tenant).docs/migration.md documents the adapter/job model,
fidelity tiers, and the server-side ingestion path.Site methods
provision_migration_containers, provision_migration_queue,
create_migration_job_encrypted, and get_migration_job_progress.
MigrationServerJob gained submit_encrypted, progress, and a
GetMigrationJobProgress-backed status_fn (monitor now defaults to it);
parse_progress_events reduces the event log to (status, done, total).office365.migration.package): models and
serialization for Manifest.xml / ExportSettings.xml / SystemData.xml /
UserGroupMap.xml, a PackageBuilder (deterministic GUIDs, folders, files,
versions; indented XML), FileSystemStaging, and optional BlobStaging (new [azure] extra:
azure-storage-blob; AzureBlobStaging kept as an alias). create_staging(...)
selects a backend from the container URL — the seam for a future S3 / Azure
Files staging. SharePointPackageTarget ties it together as a DataTarget — it
builds the package, stages the blobs, and submits an ingestion job (the
constructor accepts staging= alone, so a package can be built and staged
without Azure). Covers the document-library subset; the generated XML
follows the documented format but is not yet verified against a live tenant.docs/migration.md now covers which legs
need Azure (only the server-side SharePoint ingest leg, and containers can be
SharePoint-provided — no Azure account), the Staging seam, and a step-by-step
example.BlobStaging / create_staging accept an
encryption_key; every content and manifest blob is then AES-256-CBC encrypted
(unique random IV, stored as the base64 IV blob property) — required for
SharePoint-provided containers. SharePointPackageTarget forwards its
encryption_key to the default staging, and the [azure] extra now also pulls
in cryptography.migrate_library_serverside.py is a five-step,
zero-argument server-side migration (defaults to the repo sample data), and a
new tenant-free package_library.py builds the same package offline so the
manifest XML can be inspected. The migration README now explains the pipeline,
which example to run, and in what order.MigrationSession now mirrors the
Microsoft.SharePoint.MigrationTool.PowerShell cmdlets — register / get /
add_task / remove_task / show / start / stop (cancel) / unregister,
with task ids. New MigrationSettings (the Register-SPMTMigration surface,
mapped to MigrationOptions via to_options()) and MigrationTask
(FileShare/SharePoint descriptors + the SPMT JSON task format). A SharePoint
resolver builds the adapters from a task descriptor — client-side REST by
default, or the server-side Migration API with use_migration_api=True.
MigrationOptions gained created_after/modified_after date filters.FileVersions scan — the SMAT "File Versions" detail report: records each
file that carries version history (via its MajorVersion/MinorVersion), with
the SMAT columns (VersionCount, File, ScanID, …).CheckedOutFiles (checked-out files + a per-list
warning), LargeExcelFiles (Excel workbooks over the browser-open limit),
BrowserFileHandling (.htm/.html files affected by Strict handling),
LongOneDriveUrls (files whose full URL crosses the OneDrive sync limit —
backed by the new Limits.LONG_ONEDRIVE_URL, 400 chars, and the
long_onedrive_url option), ThicketFolder (a THICKET_FOLDER_UNSUPPORTED
blocker for *_file/*_files folders), and UnsupportedSiteTemplates (site
collections whose root web template is on-premises-only) — typed detail reports
sharing a SiteScanRecord base (the SMAT site-column prefix). WebTemplateType
now covers the full Get-SpoWebTemplate catalog (with a name_part helper) and
is exported from office365.sharepoint.webs.templates. New
Limits.LARGE_EXCEL_FILE (10 MB) backs the Excel threshold; AssessmentOptions
gained large_excel_bytes. The assessor's item load now also selects
File/CheckOutType/File/TimeCreated/File/TimeLastModified and expands
File/ModifiedBy/File/CheckedOutByUser; the root-web template
(RootWeb/WebTemplate) is loaded with the site-collection metadata.MATCH_PRIORITY (highest wins,
registration order breaks ties) instead of import order, and
ClientRequestException exposes the parsed .hresult / .error_type from a
SharePoint "<hresult>, <dotnet-type>" code. New
office365.sharepoint.exceptions.SharePointException catch-all, plus typed
SPFileCheckOutException (HTTP 423, distinct from the shared-lock
FileLockedException), SPListDataValidationException,
SPFieldValidationException, SPFieldValueException,
SPDuplicateValuesFoundException, SPInvalidLookupValuesException,
SPContentTypeReadOnlyException and SPContentTypeSealedException.SPListDataValidationException,
SPDuplicateValuesFoundException), which are stable across locales.
DuplicatedObjectException keeps its English message fallback only for the
opaque -1, System.Exception case. HTTP 423 is now disambiguated: a
SPFileCheckOutException (check-out, releasable) is typed separately from the
shared coauthoring FileLockedException; a 423 with no other signal still maps
to FileLockedException.from_* is now the streaming entry
(returns ImportResult) and queue_* (queue_records/queue_dataframe) is
the deferred queue-all path. Removed import_from/import_records/
import_dataframe/import_from_file (use from_records/from_dataframe/
from_file) and to_json_file/from_json_file (use export_to(..., format= "json")/from_json). FieldCollection.from_dataframe →
ensure_from_dataframe; the JSON-array file format is registered as json
(json_file kept as an alias).ClientObjectCollection onto a new
RecordCollection base (inherited by every typed EntityCollection). Formats
are resolved through runtime.converters.registry; unified export_to(...) /
import_from(...) sit alongside the existing to_*/from_* conveniences.
Keyed skip/upsert is a pluggable UpsertTarget (runtime.converters.upsert)
implemented for list items by ListItemUpsertTarget; the migration toolkit's
SharePointListTarget reuses it.import_from/import_records gained enforce_unique=True (mark the key column
unique) and dry_run=True (plan the create/update/skip counts without writing).List gained record facades over its items: import_from/import_dataframe/
import_records (streaming), export_to/to_dataframe (record export). The
naming is now consistent everywhere: from_* is deferred (queue-all),
import_* is the streaming ImportResult. List.from_dataframe is now
deferred (it was the streaming entry); use List.import_dataframe for the
streaming path. List.export remains the .zip package export.SharePointListSource now reuses the shared record projection
(to_records(raw=True), no JSON coercion).MigrationOptions.preserve_timestamps now defaults to False (it was True
but never implemented). preserve_timestamps/preserve_permissions are
applied client-side on a best-effort basis (see above); preserve_versions
still needs the server-side Migration API (MigrationServerJob). Enabling a
flag the adapter pair can't honor raises NotImplementedError instead of
silently no-op'ing.Site.create_migration_ingestion_job — azure_queue_report_uri and
ingestion_task_key are now optional (the API treats the queue as optional).export_to(..., page_size=...)
streams appendable formats (CSV/TSV/NDJSON/JSON) page by page — bounded memory
for large collections. With on_error="collect" and a dead_letter, a
chunk is executed record-by-record so each failing row is dead-lettered as
{"row": ..., "error": ..., "record": {...}}.SPQueryThrottledException with actionable guidance; Folder.get_files now
pages (so >5,000-item folders work — refs #930/#936/#462);
List.get_items(query, page_size=...) pages CAML results (continuing from the
last item via ListItemCollectionPosition); List.ensure_indexed(name) /
Field.ensure_indexed() index an existing column via enableIndex (the real
fix for #427); and List.get_items warns on unpaged filter/sort queries. New
docs/large-lists.md guide (including the large-library upload caveat, #726).ListItemCollection,
FileCollection, FolderCollection) now warn once when an unpaged load
reaches the 5,000-item threshold (they may have been silently trimmed) and point
at the paged API — covering the ctx.load(folder, ["Files"]) path from
#930/#936. CamlQuery.index_candidates / List.index_candidates(query) name the
columns a query should index (propose, never mutate). List.check_query(query)
(and List.get_items(query, ..., check=True)) pre-flights a query and raises
actionable guidance naming the columns to index instead of the opaque server
500 (opt-in; performs 1–2 requests).ImportResult.run(...) aliases
execute_batch, and ImportResult.verify(source, key=...) reconciles a
source's natural keys against the target (delegating to the collection). The
pipeline and the migration toolkit now share one VerificationReport
(runtime.verification); RecordCollection.verify / List.verify generalize
verify_keys / verify_dataframe.tsv, parquet,
orc and feather (optional [parquet] extra) alongside CSV/JSON/NDJSON/
Excel/DataFrame, plus from_sql/to_sql ([sql]) and from_duckdb/
to_duckdb ([duckdb]) for bounded-memory DB streaming. Every reader/writer
accepts a path, a PathLike or an open file object (pandas parity); the
JSON-array file format is registered as json (json_file alias kept).Folder.write_dataframe("stocks.csv", df) / File.write_dataframe(df) serialize a DataFrame into a file's
content (UTF-8-BOM CSV so Excel keeps the columns, XLSX, JSON, ...), and
Folder.read_dataframe("stocks.csv") / File.read_dataframe() parse a file's
content back into a DataFrame (deferred DataFrameResult). File metadata
stays as plain properties. List.from_file("Shared Documents/stocks.csv", key=...) downloads a SharePoint-hosted CSV/XLSX and streams it into the list
(bounded, resumable, idempotent). dataframe_to_bytes/dataframe_from_bytes
are the content codecs.List.import_from(..., schema={column: FieldType}) now also coerces values into the payload shape SharePoint
expects — MultiChoice ("; "-separated or a list), Lookup/MultiLookup,
User/MultiUser ({LookupId}/{Email}), URL ({Url, Description}),
Geolocation, plus Boolean/Integer/Number/DateTime parsing. Backed by
office365.sharepoint.fields.coercion.coerce_field_value; the generic
RecordCollection.import_from(..., coerce={key: converter}) hook keeps the
runtime destination-agnostic.import_from(..., mapping={"SourceCol": "TargetField"})
renames source columns/keys before queuing (list imports rename before
field-name sanitization), so differently-named sources land in the right columns.RecordCollection.verify_keys(keys) reconciles a keyed
import against the target (returns a VerificationResult with ok/missing);
List.verify_dataframe(df, key=...) derives the keys from a DataFrame.ImportResult/import_from(..., dead_letter="dl.jsonl")
appends each collected chunk failure ({"error": ..., "records": [...]}) to a
JSONL file (with on_error="collect") for remediation.List.import_from(..., on_schema_change="evolve"| "fail") provisions columns that first appear in a later chunk (default
evolve) or rejects them (fail); backed by a per-chunk before_chunk
hook on ImportResult that runs before the chunk is queued.MigrationRunner now uses the persisted
Checkpoint.source_watermark — with MigrationOptions.incremental it skips
items at/below the watermark and advances it to the highest migrated source
modified (filesystem, SharePoint library and list sources populate it), so a
resumed incremental run only re-scans new/changed items.office365/migration/_util.py; its helpers moved to their domains:
emit_progress → runtime.operations, iso/iso_or_none/utc_now_iso →
runtime.converters.scalars, record_to_json → runtime.converters.json_file.
Report writing is now per-format (migration.report_io.write_dataset/
write_formats) instead of the CSV+JSON-coupled write_csv_json.MigrationItem carries created and the reliable system author_id/editor_id
(in addition to modified); the filesystem, SharePoint library and SharePoint
list sources populate them, and the item report exports them. Incremental
migration now has source timestamps for SharePoint libraries, and
SharePointLibraryTarget.modified() supports the target-side comparison.ensure_* (fields, lists, content
types, terms, contact folders) share new
runtime.queries.get_or_create.get_or_create/create_or_get primitives, and
accept on_conflict="skip"|"update" to reconcile an existing definition.List.ensure_field/ensure_fields and FieldCollection.from_dataframe
now return the ensured entity/entities (Field / list[Field]);
Folder.ensure_folders returns list[Folder]. List.ensure_fields_from_dataframe
was removed (use List.fields.from_dataframe).List.from_dataframe() returns an ImportResult driver instead
of the List; progress is now keyword-only and the whole frame is no longer
queued — execution happens on the chosen terminal.FunctionImport whose this parameter is Collection(X) (e.g. SP.User's
RemoveById / RemoveByLoginName) was attached to the item class X with the wrong
resource path, because the binding type name had Collection(...) stripped during
normalization. The SharePoint (v3) and Graph (v4) readers now skip collection-bound
operations — their home is the hand-written <Item>Collection class — and the 49
already-generated methods were removed from 24 item classes (e.g. User.remove_by_id,
Feature.remove, SubtitleFile.add, SitePublishingPage.set_multilingual,
SiteProperties.get_lock_state_by_id, MigrationTask.batch_*).with_transport(session=...) advertises session.auth for NTLM/SSPI, but every
SharePoint request still went through AuthenticationContext.authenticate_request,
which raised ValueError: Authentication credentials are missing or invalid when no
provider was configured. The request, the form digest fetch and the batch paths now
skip the auth context when the transport carries a handler and nothing is configured;
a configured provider still wins, and a session without auth still raises.SPListItem (live-validated). The
Migration API silently skips an SPFile unless the package also contains
the file's SPListItem (with a <Fields> member) — PackageBuilder.add_file
now emits it, and add_list_item reuses it for sharing metadata. Also: every
<User> must carry SystemId (the service schema requires it although the docs
call it optional), and DeploymentRoles must not be emitted (the target's
role definitions already exist — "Updates to system roles is not allowed").PackageBuilder.add_role_assignment now defaults object_type="2". The
service parses RoleAssignment/@ObjectType as a numeric enum (0 web, 1
list, 2 item/file); 2 was live-validated to break inheritance on a file.
Note: the grant itself (Assignment role→principal) and Author/ModifiedBy
still don't land — the target user's SystemId (SID) isn't exposed by the SPO
REST API, so the principal can't be resolved yet (parked).ClientContext(url, allow_ntlm=True)
was ignored twice over: with_user_credentials raised unconditionally instead of
delegating to AuthenticationContext.with_credentials (which already routes to
NtlmProvider when allow_ntlm is set), and pending_request() never forwarded
allow_ntlm/browser_mode to the request. with_user_credentials now delegates,
the flags are forwarded, and the retired-SAML guard still fires for SharePoint
Online (allow_ntlm=False).SharePointPackageTarget no longer emits an empty ExportSettings SiteUrl.
It fell back to an unloaded site.url (None → ""), so the API rejected the
job with There is an error in XML document (2, 62) /
Invalid URI: The URI is empty. The target now resolves the site URL (loading
Url when needed) or raises a clear error, and accepts a source_type.Manifest.xml now matches the shape the service accepts. It emitted a
SPWeb object (rejected — the service's SPObjectType excludes it) and a
<List> element. Following the working MigrationApiDemo sample it now emits
the library root SPFolder, the SPDocumentLibrary, an SPFolder per
subfolder, and an SPFile per file — with FileValue = the content blob name
(so the manifest points at the staged blobs) and ListItemIntId. Also fixed
UserGroupMap.xml → UserGroup.xml (the name the API downloads) and the
ViewFormsList namespace (…viewformlist…, not …viewformslist…).Requirements.xml / RootObjectMap.xml /
LookupListMap.xml / ViewFormsList.xml by name, and a missing one fails the
job (Unable to download Requirements.xml … (404)) even though the docs call
them optional. PackageBuilder emits all four (childless roots, plus a List
entry in RootObjectMap.xml). Failed jobs are now diagnosable:
MigrationServerJob.all_events / errors, SharePointPackageTarget.events /
errors / diagnose (which fetches and AES-decrypts the API's import log via
BlobStaging.read_manifest_blob).CreateMigrationJobEncrypted sends the AES key as base64 text.
provision_migration_containers() returns the key already base64-encoded, and
the REST payload wants that base64 string — so create_migration_job_encrypted
now passes a str key through (base64-encoding raw bytes) instead of decoding
it. Decoding produced raw bytes that the payload serializer tried to UTF-8-decode
(UnicodeDecodeError).$skip paging no longer collides with a server $skiptoken. Once the
server drives paging (__next/@odata.nextLink), the client-side $skip
fallback is disabled, fixing The $skip and $skiptoken cannot be specified at the same time on the final page of a paged collection (e.g.
items.order_by("ID").get_all(page_size=2000)).prepare hook), so a replayed or overlapping chunk (a crash between the server
commit and the checkpoint write, or a changed --chunk) could duplicate rows.
The key column is ensured and the existing keys are loaded lazily before the
first queued chunk — fresh or resumed — so key=... imports are idempotent on
every run. ImportCheckpoint also records a source signature (format, chunk
size, key columns); on a mismatch the chunk-based skip is discarded and the
source is re-scanned (the keyed skip keeps it duplicate-free).Name resolves to FileLeafRef): the column
is imported with a _ suffix and a warning is emitted.series_kind/field_type_from_kind now handle complex, timedelta, category
and object-datetime columns, and unknown kinds fall back to Text instead of
raising KeyError.A large release: a full migration toolkit (SharePoint / Outlook / Teams) with SMAT-style pre-migration assessment, first-class parallel execution ( ex
A large release: a full migration toolkit (SharePoint / Outlook / Teams)
with SMAT-style pre-migration assessment, first-class parallel execution
(execute_batch / execute_query_parallel), a data pipeline
(CSV/JSON/NDJSON/Excel + optional pandas), and a set of long-standing
SharePoint/OneDrive fixes (#881, #988, #915, #884, #793, #875).
No breaking API changes - a drop-in upgrade from 3.0.0.
sharepoint, outlookteams products: resumable MigrationJob/MigrationSession withMigrationAssessor/MigrationTenantAssessor/MailboxAssessor, typed scanLargeSites, LockedSites, MailFolders) and CSV/JSON export.execute_batch(concurrency=N) on ClientContextGraphClient, plus execute_query_parallel(concurrency=N) forRetry-After.from_csv/from_json/from_records, dynamic list-item columns, and anto_dataframe/from_dataframe).Web.ensure_list, DriveItem.ensure_folder, and typed field creators.ClientContext.clone now shares the auth context and transport.ClientQuery generics madeassess/ → migrate/ → monitor/).File.move_by_path /MoveCopyUtil.move_file_by_path; slashes are no longer percent-encodeddownload_folder paginates children$skip when no next link is returned@odata.type casting for directory collections (#921); principal pathClientRequestException.Use with_username_and_password (MSAL ROPC) instead. ACS App-Only (with_client_credentials) is marked deprecated.
First major release in over a year - and it's live on PyPI with an automated release pipeline.
with_user_credentials now raises RuntimeError for SharePoint Online. Use with_username_and_password (MSAL ROPC) instead. ACS App-Only (with_client_credentials) is marked deprecated.with_username_and_password (MSAL ROPC) for SharePoint Online authclient_runtime_context.py / read_entity.pyNameError: name 'List' is not defined in fields/collection.pysingle() and first() return types (never return None)AttributeError: 'str' object has no attribute 'get' when SharePoint returns a string error payload.travis.yml (moved to GitHub Actions)requirements.txt / requirements-dev.txt (use uv/pyproject.toml)pip install --upgrade office365-rest-python-client
Or with uv:
uv pip install --upgrade office365-rest-python-client
with_user_credentials now raises RuntimeError for SharePoint Online. Use with_username_and_password (MSAL ROPC) instead. ACS App-Only (with_client_credentials) marked as deprecated.with_username_and_password (MSAL ROPC) for SharePoint Online authSelf, X | None)datetime.utcnow() replaced with timezone-aware alternatives (Python 3.12+ deprecation)entity_type_name support, EnumType generation, template-driven model propsResourceName)client_runtime_context.py / read_entity.pyNameError: name 'List' is not defined in fields/collection.pysingle() and first() return types (never return None)AttributeError: 'str' object has no attribute 'get' when SharePoint returns a string error payload.travis.yml (use GitHub Actions instead)requirements.txt / requirements-dev.txt (use uv/pyproject.toml)uv and pyproject.tomlChangelog #953 : support Access Token Refresh for MSAL Provided Tokens by @kamilkrukowski #956 : resolve get_comments() method issue #958 : added Repl
get_comments() method issueFile.open_binary issue by @bhavberiNothing published for this version
support for Azure environments in ClientContext
ClientContextctx = ClientContext(
site_url, environment=AzureEnvironment.USGovernmentHigh
).with_client_credentials(client_id, client_secret)#950: Username and password authentication (with_user_credentials method): Added support for automatically renewing authentication cookies when the expiration period ends (after 30 minutes)
#926: support for used range has been introduced
ClientContext (USGovernment, GCC High, etc.)with_user_credentials (after 30 min expiry) (#950)Microsoft Search API enhancements
include_resource_data to SubscriptionCollection.add by @optimygmbhChangelog #835 - JSON batch request fixes
#835 - JSON batch request fixes
Changelog #788 - batch request fixes and improvements
#788 - batch request fixes and improvements
Changelog #873 : fix error when creating a choice field #872 : fix shares.by_url method #869 : Bump msal from 1.24.1 to 1.28.0 #627 : Fixes failing dr
shares.by_url methodChangelog #866 : fix path issue when addressing drive items
Changelog #847 Add a note for Entra ID permissions when updating SP metadata fields #850 Fixes issue of str raising exception when DecodeUrl is None #
SharePoint resources addressing enhancements
client = GraphClient.with_token_interactive(
tenant_name_or_id, app_client_id, admin_principal_name
)
resource = client.service_principals.get_by_name("Microsoft Graph")
app = client.applications.get_by_app_id(app_client_id)
resource.grant_application(app, "MailboxSettings.Read").execute_query()client = GraphClient.with_token_interactive(
tenant_name_or_id, app_client_id, admin_principal_name
)
resource = client.service_principals.get_by_name("Microsoft Graph")
app_role = "User.Read.All"
user = client.users.get_by_principal_name(test_user_principal_name)
resource.grant_delegated(app_client_id, user, app_role).execute_query()`python cert_credentials = { "tenant": tenant_name, "client_id": client_id, "thumbprint": cert_thumbprint, "cert_path": "selfsignkeyenc.pem", "passphr
ClientContext.with_client_certificate methodExample:
cert_credentials = {
"tenant": tenant_name,
"client_id": client_id,
"thumbprint": cert_thumbprint,
"cert_path": "selfsignkeyenc.pem",
"passphrase": "Password",
}
ctx = ClientContext(site_url).with_client_certificate(**cert_credentials)
current_web = ctx.web.get().execute_query()
ClientContext.with_client_certificate (#836)Example: create a folder with a color
Example: create a folder with a color
ctx = ClientContext(site_url).with_credentials(user_credentials)
root_folder = ctx.web.default_document_library().root_folder
folder = root_folder.folders.add(
"archive", color_hex=FolderColors.DarkGreen
).execute_query()
```python client = GraphClient.with_username_and_password( tenant_name, client_id, username, password ) drive_item = client.me.drive.root.get_by_path(
Example: get Range
client = GraphClient.with_username_and_password(
tenant_name, client_id, username, password
)
drive_item = client.me.drive.root.get_by_path("Financial Report.xlsx")
worksheets = drive_item.workbook.worksheets.get().execute_query()
if len(worksheets) == 0:
sys.exit("No worksheets found")
worksheet_range = worksheets["Sheet1"].range(address="A1:B3").execute_query()
### Changelog - #794 SharePoint authentication support with GCC High Environments by @DEADSCOP - #802 fix listing folders insted of listing files by @
### Changelog - #791 fix: do not reuse same instance for recipient by @byenilmez - #787 support for file_name parameter in File.copyto and File.copyto
file_name parameter in File.copyto and File.copyto_using_path methodsparent_folder and parent_collection empty when retrieving file using ctx.web.get_file_by_server_relative_urlfile_name parameter to File.copyto and File.copyto_using_path (#787)TypedDict on Python 3.7 (#781)parent_folder / parent_collection empty when retrieving file by server-relative URL (#764)### Changelog - #762: Missing dependency typing_extensions on Python by @chludwig-haufe - #764: file and folder addresing methods fixes - #765: CI & L
## Changelog - #757: Conditionally import ParamSpec from typing_extensions by @thaiphv
ParamSpec from typing_extensions (#757)introduced GraphClient.with_client_secret and GraphClient.with_username_and_password methods to initialize the client, refer below examples
Folder.copy_to_using_path methodmypy and pyright type checkers) by @kellerzaFile.download_session method fixGraphClient.with_client_secret and GraphClient.with_username_and_password methods to initialize the client, refer below examplesGraphClient client using user namename and password flow:from office365.graph_client import GraphClient
client = GraphClient.with_username_and_password(
"contoso.onmicrosoft.com", client_id, username, password
)
GraphClient with client secret:from office365.graph_client import GraphClient
client = GraphClient.with_client_secret("contoso.onmicrosoft.com", client_id, client_secret)
## Changelog - #723: fixes FieldCollection.add_dependent_lookup_field by @pclasen-eb - #722: fixes 404 error with Web.get_file_by_server_relative_path
FieldCollection.add_dependent_lookup_field by @pclasen-ebWeb.get_file_by_server_relative_path methodSupport for Interactive authentication via ClientContext.with_interactive method introduced
ClientContext.with_interactive method introducedClientContext clienttenant = "contoso.onmicrosoft.com"
ctx = ClientContext(site_url).with_interactive(tenant, client_id)
me = ctx.web.current_user.get().execute_query()
print(me.login_name)
When addressing a file or folder, in addition to _server relative url_ format /sites/mysite/folder/filename.ext, it is supported to specify url in _si
When addressing a file or folder, in addition to server relative url format /sites/mysite/folder/filename.ext, it is supported to specify url in site or web relative format now: folder/filename.ext, for example:
ctx = ClientContext(site_url).with_credentials(credentials)
file_url = 'Shared Documents/Report.xlsx'
file = ctx.web.get_file_by_server_relative_url(file_url).get().execute_query()
Use ensure_created method which polls for team status to determine whether it has been created:
client = GraphClient(acquire_token)
new_team = client.teams.create(team_name).ensure_created().execute_query()
## Changelog - #672: Error handling for when response is not present by @akrymskiy - #667: add ability to pass b64 formatted string when adding attach
ClientResult (return type) between calls when passing to execute_query method by @vgremSharePoint API search namespace enhancements
For methods from SharePoint API search namespace those return type contains value pf SimpleDataTable type, cell values getting addressed like this:
for row in results.Table.Rows:
print(row.Cells["Path"]) # prints cell value for `Path` property
Here is an complete example
ctx = ClientContext(site_url).with_credentials(user_credentials)
result = ctx.search.query("contentclass:STS_Site").execute_query()
results = result.value.PrimaryQueryResult.RelevantResults
for row in results.Table.Rows:
print(row.Cells["Path"]) # prints site url
OneDrive enhancements in terms of Excel support
Create a new table example
from office365.graph_client import GraphClient
from office365.onedrive.workbooks.tables.rows.row import WorkbookTableRow
file_name = "Financial Sample.xlsx"
client = GraphClient(acquire_token)
workbook = client.me.drive.root.get_by_path(file_name).workbook
table = workbook.tables.add("A1:C10", True).execute_query()
List rows example
from office365.graph_client import GraphClient
from office365.onedrive.workbooks.tables.rows.row import WorkbookTableRow
file_name = "Financial Sample.xlsx"
client = GraphClient(acquire_token)
workbook = client.me.drive.root.get_by_path(file_name).workbook
table = workbook.worksheets["Sheet1"].tables["financials"].get().execute_query()
print(table.name)
# read table content
rows = table.rows.get().execute_query()
for r in rows: # type: WorkbookTableRow
print(r.values)
enhancements for SharePoint & OneDrive APIs
ClientRuntimeContext.execute_query_retry method by @jneuendorfwith_client_certificate methodcert_path = 'selfsigncert.pem'
with open(cert_path, 'r') as f:
private_key = open(cert_path).read()
cert_credentials = {
'tenant': test_tenant,
'client_id': '--client id--',
'thumbprint': '--thumbprint--',
'private_key': private_key
}
ctx = ClientContext(test_team_site_url).with_client_certificate(**cert_credentials)
field_value = ImageFieldValue(image_url)
list_item.set_property(field_name, field_value).update().execute_query()
introduction of ClientObjectCollection.get_all method to retrieve all the items in a collection, regardless of the size, for example:
ClientObjectCollection.get_all method to retrieve all the items in a collection, regardless of the size, for example:
def print_progress(items):
"""
:type items: office365.sharepoint.listitems.collection.ListItemCollection
"""
print("Items read: {0}".format(len(items)))
page_size = 500
ctx = ClientContext(site_url).with_credentials(client_credentials)
large_list = ctx.web.lists.get_by_title(list_title)
all_items = target_list.items.get_all(page_size, print_progress).execute_query()
ClientObjectCollection.paged methodpage_size = 500
ctx = ClientContext(site_url).with_credentials(client_credentials)
large_list = ctx.web.lists.get_by_title(list_title)
paged_items = large_list.items.paged(page_size, page_loaded=print_progress).get().execute_query()
for index, item in enumerate(paged_items): # type: int, ListItem
print("{0}: {1}".format(index, item.id))
Folder.copy_to_using_path methodbetter support for Sharing namespace in SharePoint API
QueryOptions.build method, kudos to @jjlonemanSharing namespace in SharePoint APISite Designs and Site Scripts in SharePoint APIExample: Share an anonymous access link with view permissions to a file
ctx = ClientContext(site_url).with_credentials(credentials)
file = ctx.web.get_file_by_server_relative_url(file_url)
result = target_file.share_link(SharingLinkKind.AnonymousView).execute_query()
Example: create a new site script that apply a custom theme
ctx = ClientContext(site_url).with_credentials(credentials)
site_script = {
"$schema": "schema.json",
"actions": [
{
"verb": "applyTheme",
"themeName": "Contoso Theme"
}
],
"bindata": {},
"version": 1
}
result = SiteScriptUtility.create_site_script(ctx, "Contoso theme script", "", site_script).execute_query()
ListItem.validate_update_list_item method: setting dates_in_utc as an _optional_ parameter
SharePoint API:
ListItem.validate_update_list_item method: setting dates_in_utc as an optional parameter (#509)Search namespace enhancements #496SearchService.query methodctx = ClientContext(site_url).with_credentials(credentials)
search = SearchService(ctx)
result = search.query("IsDocument:1").execute_query()
SearchService.post_query methodctx = ClientContext(site_url).with_credentials(credentials)
search = SearchService(ctx)
request = SearchRequest(query_text="IsDocument:1",
sort_list=[Sort("LastModifiedTime", 1)],
select_properties=["Path", "LastModifiedTime"],
row_limit=20)
result = search.post_query(request).execute_query()
ListItem introduced support for system metadata update
SharePoint API:
ListItem introduced support for system metadata update (#330)SiteProperies.update method bug fix #480Web.ensure_folder_path method bug fix #452ListItem system updatePrefer ListItem.validate_update_list_item method which supports overriding system metadata,
Here is an example which demonstrates how to update list item system metadata (namely Author and Modified field values):
ctx = ClientContext(site_url).with_credentials(credentials)
list_tasks = ctx.web.lists.get_by_title("Tasks")
items = list_tasks.items.get().top(1).execute_query()
if len(items) == 0:
sys.exit("No items for update found")
item_to_update = items[0] # type: ListItem
author = ctx.web.site_users.get_by_email(user_principal_name)
modified_date = datetime.utcnow() - timedelta(days=3)
result = item_to_update.validate_update_list_item({
"Author": FieldUserValue.from_user(author),
"Modified": modified_date
}).execute_query()
has_any_error = any([item.HasException for item in result.value])
if has_any_error:
print("Item update completed with errors, for details refer 'ErrorMessage' property")
else:
print("Item has been updated successfully")
```python from office365.graph_client import GraphClient from office365.onedrive.driveitems.driveItem import DriveItem
Example:
from office365.graph_client import GraphClient
from office365.onedrive.driveitems.driveItem import DriveItem
def print_download_progress(offset):
print("Downloaded '{0}' bytes...".format(offset))
client = GraphClient(acquire_token_by_username_password)
# # 1. address file by path and get file metadata
file_item = client.me.drive.root.get_by_path("archive/big_buck_bunny.mp4").get().execute_query() # type: DriveItem
# 2 download a large file (chunked file download)
with tempfile.TemporaryDirectory() as local_path:
with open(os.path.join(local_path, file_item.name), 'wb') as local_file:
file_item.download_session(local_file, print_download_progress).execute_query()
print("File '{0}' has been downloaded into {1}".format(file_item.name, local_file.name))
#430: improved support for overriding underlying HTTP request settings(such as proxy and SSL) by @juguerre
#465: SharePoint API: allow passing scopes via certificate authentication method by @theodoriss
Example
from office365.sharepoint.client_context import ClientContext
cert_settings = {
'client_id': '-- app id--',
'thumbprint': "-- cert thumbprint--",
'cert_path': 'mycert.pem'),
'scopes': ['https://contoso.onmicrosoft.com/.default']
}
ctx = ClientContext(test_site_url).with_client_certificate(test_tenant, **cert_settings)
current_web = ctx.web.get().execute_query()
print("{0}".format(current_web.url))
#461: include pytz to install dependency by @Zahlii
Microsoft Graph API: Improved support for delta queries (official docs)
Example: get incremental changes for users
client = GraphClient(acquire_token_func)
changed_users = self.client.users.delta.get().execute_query()
SharePoint API enhanced support for publishing & webhooks namespaces
publishing & webhooks namespacesThe example demonstrates how to create a Site Page
ctx = ClientContext(team_site_url).with_credentials(client_credentials)
new_page = ctx.site_pages.pages.add()
new_page.save_draft(title="My news page")
new_page.publish().execute_query()
The example demonstrates how to create a webhook to SharePoint list:
ctx = ClientContext(site_url).with_credentials(client_credentials)
push_service_url = "https://westeurope0.pushnp.svc.ms/notifications?token=526a9d28-d4ec-45b7-81b9-4e1599524784"
target_list = client.web.lists.get_by_title("Documents")
subscription = target_list.subscriptions.add(push_service_url).execute_query()
where
push_service_url - your service endpoint URL. SharePoint sends an HTTP POST to this endpoint when events occur in the specified resource
Refer Overview of SharePoint webhooks for a details.
Create plannerTask endpoint)from office365.graph_client import GraphClient
client = GraphClient(acquire_token_func)
plan = ensure_plan(client.me.planner, "My plan")
task = client.planner.tasks.add(title="New task", planId=plan.id).execute_query()
Get MIME content of a message endpoint)The example demonstrates how to download an Outlook message body in MIME format and save into a file:
client = GraphClient(acquire_token_func)
# requires Mail.ReadWrite permission
user = client.users[test_user_principal_name]
messages = user.messages.select(["id"]).top(10).get().execute_query()
with tempfile.TemporaryDirectory() as local_path:
for message in messages: # type: Message
with open(os.path.join(local_path, message.id + ".eml"), 'wb') as local_file:
message.download(local_file).execute_query() # download MIME representation of a message
initial support for Reports API has been introduced
SecurableObject.get_user_effective_permissions de-serializing response bug fixedctx = ClientContext(team_site_url).with_credentials(client_credentials)
version = ctx.web.get_file_by_server_relative_path(file_url).versions.get_by_id(512)
with open(download_path, "wb") as local_file:
file = version.download(local_file).execute_query()
Documentation:
Documentation: ListItem.GetUserEffectivePermissions method
from pprint import pprint
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.permissions.permission_kind import PermissionKind
client = ClientContext(team_site_url).with_credentials(user_credentials)
file_url = "/sites/team/Shared Documents/Guide.docx"
target_user = client.web.site_users.get_by_email(test_user_principal_name_alt)
target_file = client.web.get_file_by_server_relative_path(file_url)
result = target_file.listItemAllFields.get_user_effective_permissions(target_user).execute_query()
pprint(result.value.permission_levels) # print all permission levels
# verify whether user has Reader role to a file
if result.value.has(PermissionKind.OpenItems):
print("User has access to read a file")
Documentation: reportRoot: getOffice365ActivationsUserCounts
import msal
from office365.graph_client import GraphClient
def acquire_token_by_client_credentials():
settings = load_settings()
authority_url = 'https://login.microsoftonline.com/{0}'.format(tenant)
app = msal.ConfidentialClientApplication(
authority=authority_url,
client_id=client_id,
client_credential=client_secret
)
return app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
client = GraphClient(acquire_token_by_client_credentials)
result = client.reports.get_office365_activations_user_counts().execute_query()
Documentation: use the Microsoft Search API to search content in OneDrive and SharePoint
import msal
from office365.graph_client import GraphClient
def acquire_token_by_client_credentials():
settings = load_settings()
authority_url = 'https://login.microsoftonline.com/{0}'.format(tenant)
app = msal.ConfidentialClientApplication(
authority=authority_url,
client_id=client_id,
client_credential=client_secret
)
return app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
client = GraphClient(acquire_token_by_client_credentials)
result = client.search.query("Guide.docx", entity_types=["driveItem"]).execute_query()
Web.get_folder_by_server_relative_path return type fix @sharifhsn
generator module @rikeshtailorWeb.get_folder_by_server_relative_path return type fix @sharifhsna preliminary support for communications API has been introduced
communications API has been introducedtests)Example 1: address folder by path, where archive/2018 is a folder path:
folder_item = client.me.drive.root.get_by_path("archive/2018").get().execute_query() # type: DriveItem
Examples: list all teams
client = GraphClient(acquire_token_by_client_credentials)
teams = client.teams.get_all(["displayName"]).execute_query()
for team in teams: # type: Team
print(team.display_name)
general: compatibility with Python 2.7 (related: #326 & #275)
msal 1.12.0ListItem.update method (related #365)SharePoint API: improved support for a file and folders addressing details
SharePoint API: improved support for a file and folders addressing details
SharePoint API: support for saving taxonomy columns #353
Example: save a single-valued taxonomy field value
ctx = ClientContext(site_url).with_credentials(credentials)
tasks_list = ctx.web.lists.get_by_title("Tasks")
tax_field_value = TaxonomyFieldValue("{term-label}", "{term-guid}")
item_to_create = tasks_list.add_item({
"Title": "New task",
"{tex-column-name}": tax_field_value,
}).execute_query()
Example: save a multi-valued taxonomy field value
ctx = ClientContext(site_url).with_credentials(credentials)
tasks_list = ctx.web.lists.get_by_title("Tasks")
tax_field_value = TaxonomyFieldValue("{term-label}", "{term-guid}")
tax_field_values = TaxonomyFieldValueCollection([tax_field_value])
item_to_create = tasks_list.add_item({
"Title": "New task",
"{tex-column-name}": tax_field_values,
}).execute_query()
API improvements for Fluent interface
SharePoint API improvements: #303 (addressing versions of ListItem), hubsite namespace, File/Folder copy/move operations
ClientObject.get_property() semantics by @kraptorhubsite namespace, File/Folder copy/move operations
def acquire_token_client_credentials():
authority_url = 'https://login.microsoftonline.com/{tenant}'
app = msal.ConfidentialClientApplication(
authority=authority_url,
client_id='{client-id}',
client_credential='{client-secret}'
)
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
return result
client = GraphClient(acquire_token_client_credentials)
current_user = self.client.users['{user-principal-name}'].get() # 1.1: construct query to retrieve user details
my_drive = self.client.users['{user-principal-name}'].drive.get() # 1.2: construct query to retrieve user's drive
client.execute_batch() # 2: submit a query to the server
improved support for SharePoint API (fields, tenants namespaces)
Summary:
fields, tenants namespaces)Here are a few examples which demonstrate how to create field and set values for a different field types:
Create a multi lookup field:
lookup_field = list.fields.add_lookup_field(title=lookup_field_name,
lookup_list_id=lookup_list.properties['Id'],
lookup_field_name='Title',
allow_multiple_values=True).execute_query()
Set a multi lookup field value
field_value = FieldMultiLookupValue()
field_value.add(FieldLookupValue(lookup_id))
updated = item_to_update.set_property(lookup_field_name, field_value).update().execute_query()
The list of methods (fields namespace):
FieldCollection.add_url_field(title)FieldCollection.add_lookup_field(title, lookup_list_id, lookup_field_name, allow_multiple_values)FieldCollection.add_choice_field(title, values, multiple_values)FieldCollection.create_taxonomy_field(name, ssp_id, term_set_id, anchor_id,field_id, text_field_id, web_id, list_id)The list of methods (tenant namespace):
Tenant.get_site_health_status(site_url)requests_ntlm is included as an optional dependency
requests_ntlm is included as an optional dependencyREADME.md broken links resolvedimprovements for SAML-based claims authentication, kudos to @wreiner for the implementation and @nsmcan for the proposal #297, #272
FileCollection.create_upload_session method by @beliaev-maksim #289adal library to msal as a mandatory dependency #296sharepoint namespace, new types and methods (Tenant, ViewField, ContentType classes)OutlookClient is decommissioned and no longer availableMigration from Azure Active Directory Authentication Library (ADAL) to use the Microsoft Authentication Library (MSAL) library as a _mandatory_ depend
FieldCollection.create_field_as_xml, FieldCollection.create_taxonomy_fieldImproved support for SharePoint API Batch requests
Here is the comparison of two approaches, in the first example update operation is submitted to server per every list item, while in the second one multiple list item update operations are submitted via a since (batch) request:
list_tasks = ctx.web.lists.get_by_title("Tasks")
items = list_tasks.items.get().execute_query() #
for task_id, item in enumerate(items):
task_prefix = str(randint(0, 10000))
item.set_property("Title", f"Task {task_prefix}")
item.update()
ctx.execute_query() # <- update operation is submitted to server per every list item
list_tasks = ctx.web.lists.get_by_title("Tasks")
items = list_tasks.items.get().execute_query()
for task_id, item in enumerate(items):
task_prefix = str(randint(0, 10000))
item.set_property("Title", f"Task {task_prefix}")
item.update()
ctx.execute_batch() # <- construct a single (batch) for all list items and submit to server
list = client.web.lists.get_by_title(list_title)
list.delete_object()
client.execute_query()
list delete operation could be constructed and submitted to a server in a more compact manner:
client.web.lists.get_by_title(list_title).delete_object().execute_query()
SharePoint API Changes namespace improvements (#259), here is the list of operations:
Site.get_changesWeb.get_changesList.get_changesListItem.get_changesSharePoint API RecycleBin entity class and operations
Finally the initial support for SharePoint API Batch requests has been introduced. All insert/update/delete operations are supported.
Finally the initial support for SharePoint API Batch requests has been introduced. All insert/update/delete operations are supported.
The following example how current user and web objects could be retrieved by grouping two operations and submitting it as a single request to the server:
client = ClientContext(site_url).with_credentials(user_credentials)
current_user = client.web.currentUser
client.load(current_user)
current_web = client.web
client.load(current_web)
client.execute_batch()
which offers a new way of improving the performance.
Among another improvements and changes:
improved support for Office365 auth with ADFS by @liuliqiu
style enforcement (flake8), code formatting (isort) configuration by @domdinicola
ClientRuntimeContext.execute_query_retry method which implements Retry pattern
``` ctx = ClientContext(settings['url']).with_credentials(credentials)
ctx = ClientContext(settings['url']).with_credentials(credentials)
target_web = ctx.web.load().execute_query() #method chaining
print(target_web.url)
Web and File resources by absolute Urlabs_file_url = "https://{tenant}.sharepoint.com/sites/team/Shared Documents/sample.docx".format(tenant=tenant)
user_credentials = UserCredential(username, password)
with open(local_path, 'wb') as local_file:
file = File.from_url(abs_file_url).with_credentials(user_credentials).download(local_file).execute_query()
print("'{0}' file has been downloaded".format(file.serverRelativeUrl))
According to Documentation
Search in SharePoint includes a Search REST service you can use to add search functionality to your client and mobile applications by using any technology that supports REST web requests.
Example
The following example demonstrates how to construct a search for a documents (via Keyword Query Language syntax) and print file url (Path managed property)
from office365.runtime.auth.userCredential import UserCredential
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.search.searchRequest import SearchRequest
from office365.sharepoint.search.searchService import SearchService
from settings import settings
ctx = ClientContext.connect_with_credentials(site_url,
UserCredential(username, password))
search = SearchService(ctx)
request = SearchRequest("IsDocument:1")
result = search.post_query(request)
ctx.execute_query()
relevant_results = result.PrimaryQueryResult.RelevantResults
for i in relevant_results['Table']['Rows']:
cells = relevant_results['Table']['Rows'][i]['Cells']
print(cells[6]['Value'])
The following example demonstrates how to create a site collection via Tenant.CreateSite method:
admin_site_url = "https://{0}-admin.sharepoint.com/".format(tenant)
credentials = UserCredential(username, password)
client = ClientContext(admin_site_url).with_credentials(credentials)
tenant = Tenant(client)
props = SiteCreationProperties(target_site_url, user_principal_name)
site_props = tenant.ensure_site(props)
client.execute_query()
Creating list item and setting multi lookup field value:
create_info = {
"Title": "New task"
}
multi_lookup_value = FieldMultiLookupValue()
multi_lookup_value.add(FieldLookupValue(lookup_id))
new_item = self.target_list.add_item(create_info)
new_item.set_property("Predecessors", multi_lookup_value)
client.load(items)
client.execute_query()
Updating list item with multi choice field value:
multi_choice_value = FieldMultiChoiceValue(["In Progress"])
item_to_update.set_property("TaskStatuses", multi_choice_value)
item_to_update.update()
client.execute_query()
#210: connect to Office 365 with federated accounts, credit goes to @liuliqiu
#206: SharePoint Session API upload
initial support for Microsoft Teams API
and more
The support for Microsoft Teams API has been introduced, the following example demonstrates how how create a new team under a group which corresponds to Create team endpoint
tenant_name = "contoso.onmicrosoft.com"
client = GraphClient(tenant_name, get_token)
new_team = client.groups[group_id].add_team()
client.execute_query()
where
def get_token(auth_ctx):
"""Acquire token via client credential flow (ADAL Python library is utilized)
:type auth_ctx: adal.AuthenticationContext
"""
token = auth_ctx.acquire_token_with_client_credentials(
"https://graph.microsoft.com",
client_id,
client_secret)
return token
Here is an example which demonstrates how to retrieve data from a SharePoint list which contains more then 5k items:
def print_progress(items_read):
print("Items read: {0}".format(items_read))
ctx = ClientContext.connect_with_credentials(site_url,
ClientCredential(client_id,client_secret))
list_source = ctx.web.lists.get_by_title("Contacts_Large")
items = list_source.items
items.page_loaded += print_progress # page load event
items.page_size = 400 # specify custom page size (default is 100)
ctx.load(items)
ctx.execute_query()
#print("Items count: {0}".format(len(items)))
for item in items:
print("{0}".format(item.properties['Title']))
The example demonstrates how to connect to SharePoint Online with certificate credentials:
ctx = ClientContext.connect_with_certificate(site_url,
client_id,
thumbprint,
certificate_path)
current_web = ctx.web
ctx.load(current_web)
ctx.execute_query()
print("{0}".format(current_web.url))
Refer how-to page for a more details
In terms of file and folder operations the following methods have been introduced:
File.download(file_object) - download a file content into a file object (SharePoint API)Folder.copyto(new_folder_url, overwrite) - copy a folder (SharePoint API)Folder.moveto(new_folder_url, flags) move a folder (SharePoint API)DriveItem.download(file_object) - downloads a file content into a file object (OneDrive API)DriveItem.get_content() - downloads a file content (OneDrive API)Now for user credentials flow, instead of
Now for user credentials flow, instead of
ctx_auth = AuthenticationContext(url=site_url)
ctx_auth.acquire_token_for_user(username,password):
ctx = ClientContext(site_url, ctx_auth)
an authenticated client could be initialized like this:
ctx = ClientContext.connect_with_credentials(site_url, UserCredential(username,password))
and for client credentials flow (aka SharePoint App-Only) instead of
ctx_auth = AuthenticationContext(url=site_url)
ctx_auth.acquire_token_for_app(client_id,client_secret):
ctx = ClientContext(site_url, ctx_auth)
like this:
ctx = ClientContext.connect_with_credentials(site_url, ClientCredential(client_id,client_secret))
GroupSiteManager.create_group_ex - create a Team site
SPSiteManager.create - create a Communication site
Create a Team Site
client = ClientContext.connect_with_credentials(url,ClientCredential(client_id,client_secret))
site_manager = GroupSiteManager(client)
info = site_manager.create_group_ex("Team Site", "teamsite", True, None)
client.execute_query()
Create a Communication site
client = ClientContext.connect_with_credentials(url,ClientCredential(client_id,client_secret))
site_manager = SPSiteManager(client)
request = SPSiteCreationRequest("CommSite", "https://contoso.sharepoint.com/sites/commsite")
response = site_manager.create(request)
client.execute_query()
Nothing published for this version
Nothing published for this version
GraphClient client has been introduced for working with Microsoft Graph endpoint
Release changes:
GraphClient client has been introduced for working with Microsoft Graph endpointintroduced support for Introduced support for SharePoint 2010 list data service, ListDataService class
introduced support for Introduced support for SharePoint 2010 list data service, ListDataService class
added methods and properties for List, ListItem and Field resources:
ListItem.validate_update_listItem method - Validates and sets the values of the specified collection of fields for the list item.ListItem.system_update methodListItem.update_overwrite_version methodListItem.parent_list navigation propertyList.fields navigation propertyFieldCollection.add methodFieldCollection.get_by_id methodFieldCollection.get_by_internal_name_or_title methodFieldCollection.get_by_title methodField.update methodField.delete methodAdded support for NTLM authentication #110
Added support for NTLM authentication #110
Switch context per web #111
All the credit goes to @Aisbergg for those changes!
Outlook client transitioned from Office 365 Outlook REST API to Microsoft Graph-based Outlook REST API
Outlook client transitioned from Office 365 Outlook REST API to Microsoft Graph-based Outlook REST API
introduced support for for Resource Owner Password Credential (ROPC) flow, which is suitable for scenarios where BasicAuth (discontinued nowadays) was utilized before
Introduced support for SharePoint authentication via OAuth (app principal) and SharePoint CAML queries
Introduced support for SharePoint authentication via OAuth (app principal) and SharePoint CAML queries
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →