Skip to main content

Common tasks with SharePoint REST service

Project description

GitHub Actions CI PyPI - License PyPI - Version PyPI - Wheel Supported Versions

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 Site connection with NTLM auth and automatic form digest retrieval.
  • List helper for:
    • Add / update (MERGE) / delete items
    • Use OData params to control item responses
    • Add attachments to items
  • Library helper 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"})

    # 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")

    # 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 requests.HTTPError Unauthorized (401) exception
  • For user managed connections, call the connect method to start a session and close method 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

Notes

  • User should have permissions to the list to be accessed. Updates require contribute or higher level access.
  • Unauthorized user will return requests.HTTPError Unauthorized (401) exception
  • For user managed connections, call the connect method to start a session and close method to end the session

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. See the docs for supported 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

Notes

  • User should have permissions to the library to be accessed. Updates require contribute or higher level access.
  • Unauthorized user will return requests.HTTPError Unauthorized (401) exception
  • For user managed connections, call the connect method to start a session and close method to end the session

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 (Optional) - 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 (Optional) - 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 (Optional) - 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 (Optional) - 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 (Optional) - Additional path folder names for nested folders

Returns: bool - Delete succeeded

Usage:

contracts.delete_folder('2025', 'January')

Library.list_contents(params, *subfolders)

List contents of a SharePoint document library.

Parameters

  • params: dict[str, str | int] - OData params. See the docs for supported OData params.
  • *subfolders: str (Optional) - 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 (Optional) - 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 response record counts 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

License

MIT 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

sppyte-0.1.6.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sppyte-0.1.6-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

Details for the file sppyte-0.1.6.tar.gz.

File metadata

  • Download URL: sppyte-0.1.6.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

Hashes for sppyte-0.1.6.tar.gz
Algorithm Hash digest
SHA256 0c2f5a2476fb6c34cb7dd33ca36b2333025ccb3890ca32d427913a2a541fdc7c
MD5 9399aab73123ce3bfbaf65a9bbe83035
BLAKE2b-256 3dc76c63feec4f910124ec6855bd60474f4e6acffcb8d153ca03304c80d5564a

See more details on using hashes here.

Provenance

The following attestation bundles were made for sppyte-0.1.6.tar.gz:

Publisher: publish.yml on B-Jones-RFD/sppyte

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sppyte-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: sppyte-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 11.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for sppyte-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 5ffebeab3132520813faf7e991f0b54e9d6c9b86d8baf80e763fd69116b6dfaa
MD5 242859da94c6cf474aebf75e0e50ac22
BLAKE2b-256 74faf16fa7d9ea29643f48a71d83b3bdc640872cf922c98584d1eb31185e8d52

See more details on using hashes here.

Provenance

The following attestation bundles were made for sppyte-0.1.6-py3-none-any.whl:

Publisher: publish.yml on B-Jones-RFD/sppyte

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page