Common tasks with SharePoint REST service
Project description
Sppyte
A tiny, explicit Python helper for working with legacy SharePoint REST endpoints using and NTLM authentication. Sppyte keeps a very thin abstraction so you can reason about the underlying HTTP calls without surprises.
⚠️ This client uses NTLM (
requests-ntlm) and is best suited for SharePoint on-prem or environments where NTLM is configured. SharePoint Online typically uses different auth flows.
Table of Contents
Features
- Simple
Siteconnection with NTLM auth and automatic form digest retrieval. Listhelper for:- Add / update (MERGE) / delete items
- Use OData params to control item responses
- Add attachments to items
Libraryhelper for:- Add / delete folders
- Upload / download / delete documents
- List folder contents and control output with OData params
Installation
pip install sppyte
Quickstart
from sppyte import Site
HOST = "https://sharepoint.example.com"
SITE = "/sites/parrots" # relative path
USER = "norweigian"
PASS = "••••••••"
with Site(HOST, SITE, USER, PASS) as site:
# ---------------- Lists ----------------
pets = site.list("Pets")
# Add an item (metadata type is auto-inferred if omitted)
new_id = pets.add_item({"Title": "Norweigian Blue"})
print("Created item:", new_id)
# Update (MERGE) the item
pets.update_item(new_id, {"Title": "Polly"})
# Attach a file
with open("notes.txt", "rb") as file_handle:
pets.add_attachment(new_id, "notes.txt", file_handle)
# Fetch one item
item = pets.get_item(new_id)
# Query contents (use any OData params you need)
r = pets.get_contents({
"$select": "Id,Title,Created",
"$top": 5,
"$orderby": "Created desc",
})
# Delete the item
pets.delete_item(new_id)
# --------------- Libraries -------------
docs = site.library("Shared Documents")
# Ensure a nested folder path exists
docs.add_folder("Napping", "2025")
# Upload a document
with open("report.pdf", "rb") as file_handle:
unique_id = docs.add_document("report.txt", file_handle, "Napping", "2025")
print("Uploaded doc UniqueId:", unique_id)
# List files in folder
files = docs.list_contents({"$select": "Name,TimeCreated"}, "Napping", "2025")
# Download a document
document = docs.get_document("report.txt", "Napping", "2025")
contents = document.decode()
# Delete a document
docs.delete_document("report.txt", "Napping", "2025")
API Overview
Site
Models a session connected to a SharePoint site.
sppyte.Site(host, site, username, password)
Initiate a SharePoint site session.
Parameters
- host: str - SharePoint site host (protocol://domain)
- site: str - SharePoint site relative url
- username: str - SharePoint site username
- password: str - SharePoint site password
Returns: Site object
Usage:
from sppyte import Site
HOST = "https://sharepoint.example.com"
SITE = "/sites/Parrots" # relative path
USER = "norweigen"
PASS = "••••••••"
site = Site(HOST, SITE, USER, PASS)
try:
connection.connect() # Start session
# Do cool stuff
except HTTPError as e:
# Deal with errors
finally:
connection.close() # Close session
# With context managment is preferred
with Site(HOST, SITE, USER, PASS) as connection:
# Do cool stuff
Notes
- User should have permissions to the site, library, or list to be accessed. Updates require contribute or higher level access.
- Unauthorized user will return an
HTTPErrorUnauthorized (401) exception - For user managed connections, call the
closemethod to end the session
Site.connect()
Start a connected session.
Site.close()
Close connected session.
Site.list(name)
Get a List class instance.
Parameters
- name: str - SharePoint list name
Returns: sppyte.List
Usage:
# Given a list named 'Pets'
pets = connection.list('Pets')
Site.library(name)
Get a Library class instance.
Parameters
- name (str): SharePoint document library name
Returns: sppyte.Library
Usage:
# Given a document library named 'Contracts'
contracts = connection.library('Contracts')
List
Models a SharePoint site list.
List(name, site)
Create a list connection directly.
Parameters
- name (str): SharePoint list name
- site (Site): sppyte Site
Returns: List object
Usage:
from sppyte import Site, List
HOST = "https://sharepoint.example.com"
SITE = "/sites/Parrots" # relative path
USER = "norweigian"
PASS = "••••••••"
# Given a list named 'Pets'
site = Site(HOST, SITE, USER, PASS)
pets = List('Pets', site)
try:
pets.connect()
# Do cool stuff
except HTTPError as e:
# Deal with errors
finally:
pets.close() # Close session
# With context managment is preferred
with List('Pets', site) as pets:
# Do cool stuff
List.connect()
Start a connected session.
List.close()
Close the session.
List.add_item(item)
Add an item to a SharePoint list. Returns the added item ID.
Parameters
- item (dict[str, str | int]): Item to be added
Returns: int
Usage:
# Given a item with Title and breed fields
new_item = {
"Title": "Sonny",
"breed": "Norwiegen Blue"
}
pet_id = pets.add_item(new_item)
Notes
- Required SharePoint metadata is added automatically.
List.add_attachment(sp_id, file_name, attachment)
Add an attachment to an existing list item.
Parameters
- sp_id: int - Item to append attachment
- file_name: str - Attachment file name (include file extension)
- attachment: bytes | IO[bytes] - Stream file content
Returns: int - SharePoint item id
Usage:
# Given a item with ID of 23
pet_id = 23
with open("notes.txt", "rb") as fh:
pets.add_attachment(pet_id, "notes.txt", fh)
List.delete_item(sp_id: int)
Parameters
- sp_id: int - Item to delete
Returns: bool
Usage:
# Given a item with ID of 23
pet_id = 23
delete_success = pets.delete_item(pet_id)
List.get_contents(params)
Get contents of a SharePoint list
Parameters params: dict[str, str | int] - OData params
Returns: list[dict[str, str | int]] - JSON decoded list items
Usage:
pet_items = pets.get_contents({
"$select": "Id,Title,Created",
"$top": 500,
"$orderby": "Created desc",
})
List.get_item(sp_id)
Get SharePoint list item by ID.
Parameters
- sp_id: int - SharePoint ID to retrieve
Returns: dict[str, str | int] - JSON decoded item contents
Usage:
pet_id = 23
pet_item = pets.get(pet_id)
List.update_item(sp_id: int, patch: dict)
Update an existing list item merging properties from a patch.
Parameters
- sp_id: int - SharePoint ID to update
- patch: dict[str, str | int] - Dictionary of fields and values to update
Returns: int - Updated SharePoint ID
Usage:
pet_id = 23
patch = {
"Title": "Polly",
}
pets.update_item(pet_id, patch)
Library
Models a SharePoint site document library.
Library(name, site)
Create a document library connection directly.
Parameters
- name (str): SharePoint library name
- site (Site): sppyte Site
Returns: Library object
Usage:
from sppyte import Site, Library
HOST = "https://sharepoint.example.com"
SITE = "/sites/Parrots" # relative path
USER = "norweigen"
PASS = "••••••••"
# Given a document library named 'Contracts'
site = Site(HOST, SITE, USER, PASS)
contracts = Library('Contracts', site)
try:
contracts.connect()
# Do cool stuff
except HTTPError as e:
# Deal with errors
finally:
constracts.close() # Close session
# With context managment is preferred
with Library('Contracts', site) as contracts:
# Do cool stuff
Library.connect()
Start a connected session.
Library.close()
Close the session.
Library.add_folder(folder, *subfolders)
Add a folder to a SharePoint document library.
Parameters
- folder: str - folder to add
- *subfolders: str - additional path folder names for nested folders
Returns: bool - Add succeeded
Usage:
contracts.add_folder('2025', 'January')
Library.add_document(file_name, document, *subfolders)
Load a document to a SharePoint document library.
Parameters
- file_name: str - file name add
- document: bytes | IO[bytes] - Streamed file content
- *subfolders: str - additional path folder names for nested folders
Returns: str - Unique ID
Usage:
with open("notes.txt", "rb") as file_handler:
contracts.add_document("notes.txt", file_handler, '2025', 'January')
Library.folder_exists(folder, *subfolders)
Check if a folder exists in a SharePoint document library.
Parameters
- folder: str - Folder to add
- *subfolders: str - Additional path folder names for nested folders
Returns: bool - Folder exists
Usage:
contracts.folder_exists('2025', 'January')
Library.delete_document(file_name, *subfolders)
Delete a document from a SharePoint document library.
Parameters
- file_name: str - File name to delete
- *subfolders: str - Additional path folder names for nested folders
Returns: bool - Delete succeeded
Usage:
contracts.delete_document("notes.txt", '2025', 'January')
Library.delete_folder(folder, *subfolders)
Delete a folder from a SharePoint document library.
Parameters
- folder: str - Folder to add
- *subfolders: str - Additional path folder names for nested folders
Returns: bool - Delete succeeded
Usage:
contracts.delete_folder('2025', 'January')
Library.list_contents(params, *folders)
List contents of a SharePoint document library.
Parameters
- params: dict[str, str | int] - OData params
- *folders: str - Additional path folder names for nested folders
Returns: list[dict[str, Any]] - JSON decoded list metadata
Usage:
files = contracts.list_contents({
"$select": "Name,TimeCreated"
}, "January", "2025")
Library.get_document(file_name, *subfolders)
Read a document from a SharePoint document library.
Parameters
- file_name: str - File name to delete
- *subfolders: str - Additional path folder names for nested folders
Returns: bytes - Streamed contents
Usage:
document = constracts.get_document("notes.txt", "2025", "January")
contents = document.decode()
Errors
SessionError: raised when an HTTP session isn’t available.ResponseFormatError: raised when an expected JSON field is missing (e.g., FormDigestValue, ListItemEntityTypeFullName, ID, UniqueId).
Handle them as you would any exception:
from sppyte import ResponseFormatError, SessionError
try:
...
except (ResponseFormatError, SessionError) as e:
print("Sppyte error:", e)
Extension methods
SharePoint REST services endpoints not explicitly implemented can be accessed through the request method exposed on Site. This methods uses authentication from the current Site session and shadows request from the requests library, using a site relative url.
The get_form_digest method is provided to obtain the bearer token passed in the X-RequestDigest header for update requests.
List exposes the get_item_type method to obtain the required list item type metadata for list updates.
Notes
- OData Parameters: Methods like get_contents and list_contents accept any OData parameters via params (e.g., $select, $filter, $orderby, $top). See the docs for supported OData params.
- HTTP request errors are passed through from the requests library unhandled for transparency. See the docs for more information.
- SharePoint limits list responses to 100 records by default. Use the $top OData param for larger response counts.
Contributing
This is a pet project to save me time at work and not open for contribution.
Versioning
We use SemVer for versioning. For the versions available, see the tags on this repository.
Authors
- B Jones RFD - Package Noob - B-Jones-RFD
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sppyte-0.1.1.tar.gz.
File metadata
- Download URL: sppyte-0.1.1.tar.gz
- Upload date:
- Size: 11.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
882ab40ead27a0f87ecbf9de4feb0fc3483573a3d074d3610d7403aa459896e8
|
|
| MD5 |
318cfcb20526795b24347c41767a361b
|
|
| BLAKE2b-256 |
ba7cd8a5fa5d4350d9b28dc2db8e2141b33f3414780ad1c2f5becd86ae4987eb
|
Provenance
The following attestation bundles were made for sppyte-0.1.1.tar.gz:
Publisher:
publish.yml on B-Jones-RFD/sppyte
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sppyte-0.1.1.tar.gz -
Subject digest:
882ab40ead27a0f87ecbf9de4feb0fc3483573a3d074d3610d7403aa459896e8 - Sigstore transparency entry: 449793856
- Sigstore integration time:
-
Permalink:
B-Jones-RFD/sppyte@e2055a9f3976ef13a7ac697a28c9792ac9278862 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/B-Jones-RFD
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2055a9f3976ef13a7ac697a28c9792ac9278862 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sppyte-0.1.1-py3-none-any.whl.
File metadata
- Download URL: sppyte-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a11f7406becb40d403507a901e0e664867d56db9338a91fb5498572d6c62366
|
|
| MD5 |
ab55765c4b079df8d51f7bceff98a3b3
|
|
| BLAKE2b-256 |
3965c865dd7d816e11ac3002f13c081bacb2b43e463c48ed66ea9558978bfacc
|
Provenance
The following attestation bundles were made for sppyte-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on B-Jones-RFD/sppyte
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sppyte-0.1.1-py3-none-any.whl -
Subject digest:
0a11f7406becb40d403507a901e0e664867d56db9338a91fb5498572d6c62366 - Sigstore transparency entry: 449793900
- Sigstore integration time:
-
Permalink:
B-Jones-RFD/sppyte@e2055a9f3976ef13a7ac697a28c9792ac9278862 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/B-Jones-RFD
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2055a9f3976ef13a7ac697a28c9792ac9278862 -
Trigger Event:
release
-
Statement type: