A Python library for interacting with SharePoint document libraries using the Microsoft Graph API.
- 🔐 Authentication: MSAL-based authentication with Azure AD
- 📁 File Operations: Upload, download, delete, and move files
- 📂 Folder Operations: Download, delete, and move folders recursively
- 🔍 Search Operations: Search for files and folders by suffix (recursive and non-recursive)
- 🎯 Unified API: Single methods that auto-detect file vs folder types
- Python 3.13+
- Microsoft Azure AD application with SharePoint permissions:
Sites.ReadWrite.AllandFiles.ReadWrite.All
pip install msal requests python-dotenv1. Register an App in Azure AD
- Go to https://portal.azure.com
- Navigate to Azure Active Directory → App registrations → New registration
- Fill in:
- Name: Something like "SharePoint Python App"
- Supported account types: "Accounts in this organizational directory only"
- Redirect URI: Leave blank (not needed for app-only)
- Click Register
2. Get the Client ID
- After registration, you'll see the Overview page
- Copy the Application (client) ID - this is your
CLIENT_ID
3. Create a Client Secret
- In the same app registration, go to Certificates & secrets (left menu)
- Click New client secret
- Add a description (e.g., "Python SharePoint Access")
- Choose an expiration period (recommended: 12-24 months)
- Click Add
- IMPORTANT: Copy the Value immediately - this is your
CLIENT_SECRETand you won't be able to see it again!
4. Grant SharePoint Permissions
- Go to API permissions (left menu)
- Click Add a permission
- Choose SharePoint
- Select Application permissions (not Delegated)
- Add these permissions:
Sites.ReadWrite.All(for read/write access to all site collections)- Or
Sites.FullControl.All(for full control)
- Click Add permissions
- IMPORTANT: Click Grant admin consent for your organization (requires admin privileges)
5. Get Your Tenant ID
- Go back to Azure Active Directory → Overview
- Copy the Tenant ID (also called Directory ID)
Create a .env file in your project root:
TENANT_ID=your-tenant-id
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secretfrom sharepointer.sharepoint import SharePointManager
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Initialize SharePoint Manager
sp_manager = SharePointManager(
tenant_id=os.getenv('TENANT_ID'),
client_id=os.getenv('CLIENT_ID'),
client_secret=os.getenv('CLIENT_SECRET'),
site_name="your-site-name" # e.g., "contoso" for contoso.sharepoint.com
)
# Get site and drive IDs
sp_manager.get_site_id("/sites/YourSiteName") # Optional: specify site path
sp_manager.get_drive_id("Documents") # Document library nameInitialize the SharePoint Manager with authentication credentials.
Parameters:
tenant_id(str): Azure AD tenant IDclient_id(str): Application (client) IDclient_secret(str): Client secret valuesite_name(str): SharePoint site name (without .sharepoint.com)
Example:
sp_manager = SharePointManager(
tenant_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
client_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
client_secret="your-secret",
site_name="contoso"
)Retrieve the SharePoint site ID.
Parameters:
site_path(str, optional): Site path (e.g., "/sites/YourSite")
Returns: Site ID (str)
Retrieve the document library (drive) ID.
Parameters:
drive_name(str, optional): Document library name (default: "Documents")
Returns: Drive ID (str)
Download a file from SharePoint.
Parameters:
file_path(str): Path to file in SharePoint (e.g., "folder/data.csv")local_path(str, optional): Local path to save file. If None, returns BytesIO object
Returns:
- BytesIO object (if local_path is None)
- Local file path (str) (if local_path is provided)
Example:
# Download to memory
file_content = sp_manager.download_file("reports/data.csv")
# Download to local file
sp_manager.download_file("reports/data.csv", "/tmp/data.csv")Upload a file to SharePoint.
Parameters:
local_file_path(str): Path to local filesharepoint_path(str): Destination folder in SharePoint (empty string for root)file_name(str, optional): Name for file in SharePoint (defaults to local filename)
Returns: Response JSON from upload
Example:
sp_manager.upload_file(
local_file_path="/tmp/report.pdf",
sharepoint_path="Reports/2024",
file_name="annual_report.pdf"
)Upload a file from memory (bytes).
Parameters:
file_content(bytes): File content as bytessharepoint_path(str): Destination folder in SharePointfile_name(str): Name for the file in SharePoint
Returns: Response JSON from upload
Example:
data = b"Hello, World!"
sp_manager.upload_file_from_memory(data, "Documents", "hello.txt")Delete a file from SharePoint.
Parameters:
file_path(str): Path to file in SharePoint
Returns: True if successful
Example:
sp_manager.delete_file("old_files/temp.txt")Move a file to a different folder.
Parameters:
file_path(str): Current path to filedestination_folder_path(str): Destination folder path
Returns: True if successful
Example:
sp_manager.move_file("temp/data.csv", "Archive/2024")Recursively download an entire folder and its contents.
Parameters:
folder_path(str): Path to folder in SharePointlocal_directory(str, optional): Local directory to save folder (defaults to folder name)
Returns: Path to downloaded folder (str)
Example:
sp_manager.download_folder("Reports/2024", "/tmp/reports")Delete a folder and all its contents (permanent operation).
Parameters:
folder_path(str): Path to folder in SharePoint
Returns: True if successful
Warning: This operation is permanent and will delete all files and subfolders.
Example:
sp_manager.delete_folder("TempFolder")Move a folder to a different location.
Parameters:
folder_path(str): Current path to folderdestination_parent_folder_path(str): Destination parent folder path
Returns: True if successful
Example:
sp_manager.move_folder("OldProject", "Archive/Projects")Search for files with a specific suffix in a folder (non-recursive).
Parameters:
suffix(str): File suffix/extension (e.g., ".csv", ".pdf", "txt")folder_path(str, optional): Folder to search in (empty string for root)
Returns: List of ItemInfo objects
Example:
# Search for CSV files in root
csv_files = sp_manager.search_files_by_suffix(".csv")
# Search for PDFs in specific folder
pdf_files = sp_manager.search_files_by_suffix(".pdf", "Reports/2024")
# Suffix without dot is auto-added
txt_files = sp_manager.search_files_by_suffix("txt")Recursively search for files with a specific suffix.
Parameters:
suffix(str): File suffix/extensionfolder_path(str, optional): Starting folder for search
Returns: List of ItemInfo objects
Example:
# Search all CSV files recursively from root
all_csvs = sp_manager.search_files_by_suffix_recursive(".csv")
# Search recursively from specific folder
project_docs = sp_manager.search_files_by_suffix_recursive(".docx", "Projects")Search for folders with a specific suffix (non-recursive).
Parameters:
suffix(str): Folder suffix (e.g., ".gdb", ".bundle")folder_path(str, optional): Folder to search in
Returns: List of ItemInfo objects
Example:
# Search for GDB folders in root
gdb_folders = sp_manager.search_folders_by_suffix(".gdb")
# Search in specific location
backup_folders = sp_manager.search_folders_by_suffix(".backup", "Archive")Recursively search for folders with a specific suffix.
Parameters:
suffix(str): Folder suffixfolder_path(str, optional): Starting folder for search
Returns: List of ItemInfo objects
Example:
# Search all .gdb folders recursively
all_gdb = sp_manager.search_folders_by_suffix_recursive(".gdb")Download a file or folder (auto-detects type).
Parameters:
item_path(str): Path to item in SharePointlocal_path(str, optional): Local path to save item
Returns:
- For files: BytesIO or local file path
- For folders: Path to downloaded folder
Example:
# Downloads file or folder automatically
sp_manager.download_item("SomeItem")Delete a file or folder (auto-detects type).
Parameters:
item_path(str): Path to item in SharePoint
Returns: True if successful
Example:
sp_manager.delete_item("OldItem")Move a file or folder (auto-detects type).
Parameters:
item_path(str): Current path to itemdestination_folder_path(str): Destination folder path
Returns: True if successful
Example:
sp_manager.move_item("SomeItem", "Archive")Data class containing item information from SharePoint.
Attributes:
name(str): Item namepath(str): Full path to itemsize(int): Size in bytesmodified(str): Last modified datetime (ISO format)id(str): SharePoint item IDwebUrl(str): Web URL to item
Aliases: FileInfo and FolderInfo are aliases for ItemInfo (backward compatibility)
Example:
files = sp_manager.search_files_by_suffix(".csv")
for file in files:
print(f"Name: {file.name}")
print(f"Path: {file.path}")
print(f"Size: {file.size} bytes")
print(f"Modified: {file.modified}")
print(f"URL: {file.webUrl}")from sharepointer.sharepoint import SharePointManager
import os
from dotenv import load_dotenv
# Load configuration
load_dotenv()
# Initialize
sp = SharePointManager(
tenant_id=os.getenv('TENANT_ID'),
client_id=os.getenv('CLIENT_ID'),
client_secret=os.getenv('CLIENT_SECRET'),
site_name="contoso"
)
# Setup site and drive
sp.get_site_id("/sites/TeamSite")
sp.get_drive_id("Documents")
# Search for files
csv_files = sp.search_files_by_suffix_recursive(".csv", "Reports")
print(f"Found {len(csv_files)} CSV files")
# Download files
for file in csv_files:
content = sp.download_file(file.path)
print(f"Downloaded: {file.name}")
# Upload a file
sp.upload_file("/tmp/report.pdf", "Reports/2024", "annual_report.pdf")
# Search for folders
gdb_folders = sp.search_folders_by_suffix(".gdb")
if gdb_folders:
# Download first matching folder
folder = gdb_folders[0]
sp.download_folder(folder.name, "/tmp/geodatabase")
# Move items
sp.move_item("OldData.csv", "Archive/2023")
# Clean up old files
sp.delete_item("TempFolder")All methods raise exceptions on errors. Common exceptions:
Exception: Generic errors (e.g., "Drive ID not set")requests.exceptions.HTTPError: HTTP errors from Graph API- 404: Item not found
- 401: Authentication failed
- 403: Permission denied
Example:
try:
sp.download_file("nonexistent.txt")
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404:
print("File not found")
else:
print(f"Error: {e.response.status_code}")
except Exception as e:
print(f"General error: {str(e)}")The library includes comprehensive pytest tests:
# Run all tests
pytest tests/
# Run specific test file
pytest tests/test_file_operations.py
# Run with verbose output
pytest tests/ -v
# Run specific test
pytest tests/test_file_operations.py::TestDownloadFile::test_download_file_to_memorytests/test_authentication_and_init.py- Authentication and initializationtests/test_file_operations.py- File upload, download, delete, movetests/test_folder_operations.py- Folder operations and searchestests/test_search_operations.py- File search operationstests/test_dataclasses.py- ItemInfo dataclass teststests/test_url_helpers.py- URL construction helperstests/conftest.py- Pytest fixtures and configuration
# ✅ Good
sp.get_site_id()
sp.get_drive_id()
sp.download_file("file.txt")
# ❌ Bad - will raise "Drive ID not set" error
sp.download_file("file.txt")# Both work the same
sp.search_files_by_suffix(".csv")
sp.search_files_by_suffix("csv") # Dot is added automatically# Auto-detects if item is file or folder
sp.download_item("UnknownItem")
sp.delete_item("UnknownItem")
sp.move_item("UnknownItem", "Archive")# Root folder
sp.search_files_by_suffix(".csv", "") # Root
sp.search_files_by_suffix(".csv") # Also root (default)
# Nested folders (no leading slash)
sp.search_files_by_suffix(".csv", "Reports/2024")- Large File Upload: Files larger than 4MB should use the upload session API (not currently implemented)
- Rate Limiting: The Microsoft Graph API has rate limits. Consider implementing retry logic for production use
- Concurrent Operations: No built-in concurrency support. Implement your own if needed
- Permissions: Requires appropriate SharePoint permissions in Azure AD
Problem: "Authentication failed" error
Solutions:
- Verify Azure AD app credentials
- Check API permissions are granted and admin consented
- Ensure client secret hasn't expired
Problem: "Drive ID not set" error
Solution:
sp.get_drive_id() # Call this before any file/folder operationsProblem: Item not found errors
Solutions:
- Verify item path is correct (no leading slash for items)
- Check item exists in SharePoint
- Ensure you have permissions to access the item
Problem: Can't connect to site
Solutions:
# If full URL: contoso.sharepoint.com
site_name = "contoso"
# If full URL: contoso.sharepoint.com/sites/TeamSite
site_name = "contoso"
site_path = "/sites/TeamSite"- Initial release
- File operations (upload, download, delete, move)
- Folder operations (download, delete, move)
- Search operations (files and folders, recursive and non-recursive)
- Unified operations (auto-detect file/folder type)
- ItemInfo dataclass with backward compatible aliases
- Helper method to eliminate code duplication
- Comprehensive test suite (78 tests)
[Add your license here]
For issues, questions, or contributions, please [add contact information or repository link].