# SharePoint Manager
A Python library for interacting with SharePoint document libraries using the Microsoft Graph API.
## Features
- 🔐 **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
## Installation
### Prerequisites
- Python 3.13+
- Microsoft Azure AD application with SharePoint permissions: `Sites.ReadWrite.All` and `
Files.ReadWrite.All`
### Dependencies
```bash
pip install msal requests python-dotenv
```
## Configuration
### 1. Azure AD App Registration
#### Steps to Register an App in Azure AD and Get Credentials:
**1. 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_SECRET` and 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)
### 2. Environment Variables
Create a `.env` file in your project root:
```env
TENANT_ID=your-tenant-id
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
```
## Quick Start
```python
from 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 name
```
## API Reference
### Authentication & Initialization
#### `SharePointManager(tenant_id, client_id, client_secret, site_name)`
Initialize the SharePoint Manager with authentication credentials.
**Parameters:**
- `tenant_id` (str): Azure AD tenant ID
- `client_id` (str): Application (client) ID
- `client_secret` (str): Client secret value
- `site_name` (str): SharePoint site name (without .sharepoint.com)
**Example:**
```python
sp_manager = SharePointManager(
tenant_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
client_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
client_secret="your-secret",
site_name="contoso"
)
```
#### `get_site_id(site_path="")`
Retrieve the SharePoint site ID.
**Parameters:**
- `site_path` (str, optional): Site path (e.g., "/sites/YourSite")
**Returns:** Site ID (str)
#### `get_drive_id(drive_name="Documents")`
Retrieve the document library (drive) ID.
**Parameters:**
- `drive_name` (str, optional): Document library name (default: "Documents")
**Returns:** Drive ID (str)
---
### File Operations
#### `download_file(file_path, local_path=None)`
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:**
```python
# 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_file(local_file_path, sharepoint_path, file_name=None)`
Upload a file to SharePoint.
**Parameters:**
- `local_file_path` (str): Path to local file
- `sharepoint_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:**
```python
sp_manager.upload_file(
local_file_path="/tmp/report.pdf",
sharepoint_path="Reports/2024",
file_name="annual_report.pdf"
)
```
#### `upload_file_from_memory(file_content, sharepoint_path, file_name)`
Upload a file from memory (bytes).
**Parameters:**
- `file_content` (bytes): File content as bytes
- `sharepoint_path` (str): Destination folder in SharePoint
- `file_name` (str): Name for the file in SharePoint
**Returns:** Response JSON from upload
**Example:**
```python
data = b"Hello, World!"
sp_manager.upload_file_from_memory(data, "Documents", "hello.txt")
```
#### `delete_file(file_path)`
Delete a file from SharePoint.
**Parameters:**
- `file_path` (str): Path to file in SharePoint
**Returns:** True if successful
**Example:**
```python
sp_manager.delete_file("old_files/temp.txt")
```
#### `move_file(file_path, destination_folder_path)`
Move a file to a different folder.
**Parameters:**
- `file_path` (str): Current path to file
- `destination_folder_path` (str): Destination folder path
**Returns:** True if successful
**Example:**
```python
sp_manager.move_file("temp/data.csv", "Archive/2024")
```
---
### Folder Operations
#### `download_folder(folder_path, local_directory=None)`
Recursively download an entire folder and its contents.
**Parameters:**
- `folder_path` (str): Path to folder in SharePoint
- `local_directory` (str, optional): Local directory to save folder (defaults to folder name)
**Returns:** Path to downloaded folder (str)
**Example:**
```python
sp_manager.download_folder("Reports/2024", "/tmp/reports")
```
#### `delete_folder(folder_path)`
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:**
```python
sp_manager.delete_folder("TempFolder")
```
#### `move_folder(folder_path, destination_parent_folder_path)`
Move a folder to a different location.
**Parameters:**
- `folder_path` (str): Current path to folder
- `destination_parent_folder_path` (str): Destination parent folder path
**Returns:** True if successful
**Example:**
```python
sp_manager.move_folder("OldProject", "Archive/Projects")
```
---
### Search Operations
#### File Search
##### `search_files_by_suffix(suffix, folder_path="")`
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:**
```python
# 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")
```
##### `search_files_by_suffix_recursive(suffix, folder_path="")`
Recursively search for files with a specific suffix.
**Parameters:**
- `suffix` (str): File suffix/extension
- `folder_path` (str, optional): Starting folder for search
**Returns:** List of `ItemInfo` objects
**Example:**
```python
# 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")
```
#### Folder Search
##### `search_folders_by_suffix(suffix, folder_path="")`
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:**
```python
# 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")
```
##### `search_folders_by_suffix_recursive(suffix, folder_path="")`
Recursively search for folders with a specific suffix.
**Parameters:**
- `suffix` (str): Folder suffix
- `folder_path` (str, optional): Starting folder for search
**Returns:** List of `ItemInfo` objects
**Example:**
```python
# Search all .gdb folders recursively
all_gdb = sp_manager.search_folders_by_suffix_recursive(".gdb")
```
---
### Unified Operations (Auto-detect File/Folder)
#### `download_item(item_path, local_path=None)`
Download a file or folder (auto-detects type).
**Parameters:**
- `item_path` (str): Path to item in SharePoint
- `local_path` (str, optional): Local path to save item
**Returns:**
- For files: BytesIO or local file path
- For folders: Path to downloaded folder
**Example:**
```python
# Downloads file or folder automatically
sp_manager.download_item("SomeItem")
```
#### `delete_item(item_path)`
Delete a file or folder (auto-detects type).
**Parameters:**
- `item_path` (str): Path to item in SharePoint
**Returns:** True if successful
**Example:**
```python
sp_manager.delete_item("OldItem")
```
#### `move_item(item_path, destination_folder_path)`
Move a file or folder (auto-detects type).
**Parameters:**
- `item_path` (str): Current path to item
- `destination_folder_path` (str): Destination folder path
**Returns:** True if successful
**Example:**
```python
sp_manager.move_item("SomeItem", "Archive")
```
---
### Data Classes
#### `ItemInfo`
Data class containing item information from SharePoint.
**Attributes:**
- `name` (str): Item name
- `path` (str): Full path to item
- `size` (int): Size in bytes
- `modified` (str): Last modified datetime (ISO format)
- `id` (str): SharePoint item ID
- `webUrl` (str): Web URL to item
**Aliases:** `FileInfo` and `FolderInfo` are aliases for `ItemInfo` (backward compatibility)
**Example:**
```python
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}")
```
---
## Complete Example
```python
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")
```
---
## Error Handling
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:**
```python
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)}")
```
---
## Testing
The library includes comprehensive pytest tests:
```bash
# 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_memory
```
### Test Structure
- `tests/test_authentication_and_init.py` - Authentication and initialization
- `tests/test_file_operations.py` - File upload, download, delete, move
- `tests/test_folder_operations.py` - Folder operations and searches
- `tests/test_search_operations.py` - File search operations
- `tests/test_dataclasses.py` - ItemInfo dataclass tests
- `tests/test_url_helpers.py` - URL construction helpers
- `tests/conftest.py` - Pytest fixtures and configuration
---
## Best Practices
### 1. Always Initialize Properly
```python
# ✅ 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")
```
### 2. Use Suffix Auto-Detection
```python
# Both work the same
sp.search_files_by_suffix(".csv")
sp.search_files_by_suffix("csv") # Dot is added automatically
```
### 3. Use Unified Methods When Type is Unknown
```python
# Auto-detects if item is file or folder
sp.download_item("UnknownItem")
sp.delete_item("UnknownItem")
sp.move_item("UnknownItem", "Archive")
```
### 4. Handle Paths Consistently
```python
# 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")
```
---
## Limitations
1. **Large File Upload**: Files larger than 4MB should use the upload session API (not currently implemented)
2. **Rate Limiting**: The Microsoft Graph API has rate limits. Consider implementing retry logic for production use
3. **Concurrent Operations**: No built-in concurrency support. Implement your own if needed
4. **Permissions**: Requires appropriate SharePoint permissions in Azure AD
---
## Troubleshooting
### Authentication Errors
**Problem:** "Authentication failed" error
**Solutions:**
- Verify Azure AD app credentials
- Check API permissions are granted and admin consented
- Ensure client secret hasn't expired
### Drive ID Not Set
**Problem:** "Drive ID not set" error
**Solution:**
```python
sp.get_drive_id() # Call this before any file/folder operations
```
### 404 Not Found
**Problem:** 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
### Site Name Issues
**Problem:** Can't connect to site
**Solutions:**
```python
# If full URL: contoso.sharepoint.com
site_name = "contoso"
# If full URL: contoso.sharepoint.com/sites/TeamSite
site_name = "contoso"
site_path = "/sites/TeamSite"
```
---
## Changelog
### Version 1.0.0 (Current)
- 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)
---
## License
[Add your license here]
---
## Support
For issues, questions, or contributions, please [add contact information or repository link].
Raw data
{
"_id": null,
"home_page": null,
"name": "sharepointer",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.13",
"maintainer_email": null,
"keywords": "automation, file-management, office365, python, sharepoint",
"author": null,
"author_email": "Wouter van Riel <wouter.van.riel@infralytics.org>",
"download_url": "https://files.pythonhosted.org/packages/23/cc/6fc6da5fd781fc5094b9759ccb3e78fe8df8de33c41ad6ef7f068875f577/sharepointer-0.1.1.tar.gz",
"platform": null,
"description": "# SharePoint Manager\n\nA Python library for interacting with SharePoint document libraries using the Microsoft Graph API.\n\n## Features\n\n- \ud83d\udd10 **Authentication**: MSAL-based authentication with Azure AD\n- \ud83d\udcc1 **File Operations**: Upload, download, delete, and move files\n- \ud83d\udcc2 **Folder Operations**: Download, delete, and move folders recursively\n- \ud83d\udd0d **Search Operations**: Search for files and folders by suffix (recursive and non-recursive)\n- \ud83c\udfaf **Unified API**: Single methods that auto-detect file vs folder types\n\n## Installation\n\n### Prerequisites\n\n- Python 3.13+\n- Microsoft Azure AD application with SharePoint permissions: `Sites.ReadWrite.All` and `\nFiles.ReadWrite.All`\n\n### Dependencies\n\n```bash\npip install msal requests python-dotenv\n```\n\n## Configuration\n\n### 1. Azure AD App Registration\n\n#### Steps to Register an App in Azure AD and Get Credentials:\n\n**1. Register an App in Azure AD**\n\n- Go to https://portal.azure.com\n- Navigate to **Azure Active Directory** \u2192 **App registrations** \u2192 **New registration**\n- Fill in:\n - **Name**: Something like \"SharePoint Python App\"\n - **Supported account types**: \"Accounts in this organizational directory only\"\n - **Redirect URI**: Leave blank (not needed for app-only)\n- Click **Register**\n\n**2. Get the Client ID**\n\n- After registration, you'll see the Overview page\n- Copy the **Application (client) ID** - this is your `CLIENT_ID`\n\n**3. Create a Client Secret**\n\n- In the same app registration, go to **Certificates & secrets** (left menu)\n- Click **New client secret**\n- Add a description (e.g., \"Python SharePoint Access\")\n- Choose an expiration period (recommended: 12-24 months)\n- Click **Add**\n- **IMPORTANT**: Copy the **Value** immediately - this is your `CLIENT_SECRET` and you won't be able to see it again!\n\n**4. Grant SharePoint Permissions**\n\n- Go to **API permissions** (left menu)\n- Click **Add a permission**\n- Choose **SharePoint**\n- Select **Application permissions** (not Delegated)\n- Add these permissions:\n - `Sites.ReadWrite.All` (for read/write access to all site collections)\n - Or `Sites.FullControl.All` (for full control)\n- Click **Add permissions**\n- **IMPORTANT**: Click **Grant admin consent for your organization** (requires admin privileges)\n\n**5. Get Your Tenant ID**\n\n- Go back to **Azure Active Directory** \u2192 **Overview**\n- Copy the **Tenant ID** (also called Directory ID)\n\n### 2. Environment Variables\n\nCreate a `.env` file in your project root:\n\n```env\nTENANT_ID=your-tenant-id\nCLIENT_ID=your-client-id\nCLIENT_SECRET=your-client-secret\n```\n\n## Quick Start\n\n```python\nfrom sharepointer.sharepoint import SharePointManager\nimport os\nfrom dotenv import load_dotenv\n\n# Load environment variables\nload_dotenv()\n\n# Initialize SharePoint Manager\nsp_manager = SharePointManager(\n tenant_id=os.getenv('TENANT_ID'),\n client_id=os.getenv('CLIENT_ID'),\n client_secret=os.getenv('CLIENT_SECRET'),\n site_name=\"your-site-name\" # e.g., \"contoso\" for contoso.sharepoint.com\n)\n\n# Get site and drive IDs\nsp_manager.get_site_id(\"/sites/YourSiteName\") # Optional: specify site path\nsp_manager.get_drive_id(\"Documents\") # Document library name\n```\n\n## API Reference\n\n### Authentication & Initialization\n\n#### `SharePointManager(tenant_id, client_id, client_secret, site_name)`\n\nInitialize the SharePoint Manager with authentication credentials.\n\n**Parameters:**\n- `tenant_id` (str): Azure AD tenant ID\n- `client_id` (str): Application (client) ID\n- `client_secret` (str): Client secret value\n- `site_name` (str): SharePoint site name (without .sharepoint.com)\n\n**Example:**\n```python\nsp_manager = SharePointManager(\n tenant_id=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n client_id=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\n client_secret=\"your-secret\",\n site_name=\"contoso\"\n)\n```\n\n#### `get_site_id(site_path=\"\")`\n\nRetrieve the SharePoint site ID.\n\n**Parameters:**\n- `site_path` (str, optional): Site path (e.g., \"/sites/YourSite\")\n\n**Returns:** Site ID (str)\n\n#### `get_drive_id(drive_name=\"Documents\")`\n\nRetrieve the document library (drive) ID.\n\n**Parameters:**\n- `drive_name` (str, optional): Document library name (default: \"Documents\")\n\n**Returns:** Drive ID (str)\n\n---\n\n### File Operations\n\n#### `download_file(file_path, local_path=None)`\n\nDownload a file from SharePoint.\n\n**Parameters:**\n- `file_path` (str): Path to file in SharePoint (e.g., \"folder/data.csv\")\n- `local_path` (str, optional): Local path to save file. If None, returns BytesIO object\n\n**Returns:**\n- BytesIO object (if local_path is None)\n- Local file path (str) (if local_path is provided)\n\n**Example:**\n```python\n# Download to memory\nfile_content = sp_manager.download_file(\"reports/data.csv\")\n\n# Download to local file\nsp_manager.download_file(\"reports/data.csv\", \"/tmp/data.csv\")\n```\n\n#### `upload_file(local_file_path, sharepoint_path, file_name=None)`\n\nUpload a file to SharePoint.\n\n**Parameters:**\n- `local_file_path` (str): Path to local file\n- `sharepoint_path` (str): Destination folder in SharePoint (empty string for root)\n- `file_name` (str, optional): Name for file in SharePoint (defaults to local filename)\n\n**Returns:** Response JSON from upload\n\n**Example:**\n```python\nsp_manager.upload_file(\n local_file_path=\"/tmp/report.pdf\",\n sharepoint_path=\"Reports/2024\",\n file_name=\"annual_report.pdf\"\n)\n```\n\n#### `upload_file_from_memory(file_content, sharepoint_path, file_name)`\n\nUpload a file from memory (bytes).\n\n**Parameters:**\n- `file_content` (bytes): File content as bytes\n- `sharepoint_path` (str): Destination folder in SharePoint\n- `file_name` (str): Name for the file in SharePoint\n\n**Returns:** Response JSON from upload\n\n**Example:**\n```python\ndata = b\"Hello, World!\"\nsp_manager.upload_file_from_memory(data, \"Documents\", \"hello.txt\")\n```\n\n#### `delete_file(file_path)`\n\nDelete a file from SharePoint.\n\n**Parameters:**\n- `file_path` (str): Path to file in SharePoint\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.delete_file(\"old_files/temp.txt\")\n```\n\n#### `move_file(file_path, destination_folder_path)`\n\nMove a file to a different folder.\n\n**Parameters:**\n- `file_path` (str): Current path to file\n- `destination_folder_path` (str): Destination folder path\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.move_file(\"temp/data.csv\", \"Archive/2024\")\n```\n\n---\n\n### Folder Operations\n\n#### `download_folder(folder_path, local_directory=None)`\n\nRecursively download an entire folder and its contents.\n\n**Parameters:**\n- `folder_path` (str): Path to folder in SharePoint\n- `local_directory` (str, optional): Local directory to save folder (defaults to folder name)\n\n**Returns:** Path to downloaded folder (str)\n\n**Example:**\n```python\nsp_manager.download_folder(\"Reports/2024\", \"/tmp/reports\")\n```\n\n#### `delete_folder(folder_path)`\n\nDelete a folder and all its contents (permanent operation).\n\n**Parameters:**\n- `folder_path` (str): Path to folder in SharePoint\n\n**Returns:** True if successful\n\n**Warning:** This operation is permanent and will delete all files and subfolders.\n\n**Example:**\n```python\nsp_manager.delete_folder(\"TempFolder\")\n```\n\n#### `move_folder(folder_path, destination_parent_folder_path)`\n\nMove a folder to a different location.\n\n**Parameters:**\n- `folder_path` (str): Current path to folder\n- `destination_parent_folder_path` (str): Destination parent folder path\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.move_folder(\"OldProject\", \"Archive/Projects\")\n```\n\n---\n\n### Search Operations\n\n#### File Search\n\n##### `search_files_by_suffix(suffix, folder_path=\"\")`\n\nSearch for files with a specific suffix in a folder (non-recursive).\n\n**Parameters:**\n- `suffix` (str): File suffix/extension (e.g., \".csv\", \".pdf\", \"txt\")\n- `folder_path` (str, optional): Folder to search in (empty string for root)\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search for CSV files in root\ncsv_files = sp_manager.search_files_by_suffix(\".csv\")\n\n# Search for PDFs in specific folder\npdf_files = sp_manager.search_files_by_suffix(\".pdf\", \"Reports/2024\")\n\n# Suffix without dot is auto-added\ntxt_files = sp_manager.search_files_by_suffix(\"txt\")\n```\n\n##### `search_files_by_suffix_recursive(suffix, folder_path=\"\")`\n\nRecursively search for files with a specific suffix.\n\n**Parameters:**\n- `suffix` (str): File suffix/extension\n- `folder_path` (str, optional): Starting folder for search\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search all CSV files recursively from root\nall_csvs = sp_manager.search_files_by_suffix_recursive(\".csv\")\n\n# Search recursively from specific folder\nproject_docs = sp_manager.search_files_by_suffix_recursive(\".docx\", \"Projects\")\n```\n\n#### Folder Search\n\n##### `search_folders_by_suffix(suffix, folder_path=\"\")`\n\nSearch for folders with a specific suffix (non-recursive).\n\n**Parameters:**\n- `suffix` (str): Folder suffix (e.g., \".gdb\", \".bundle\")\n- `folder_path` (str, optional): Folder to search in\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search for GDB folders in root\ngdb_folders = sp_manager.search_folders_by_suffix(\".gdb\")\n\n# Search in specific location\nbackup_folders = sp_manager.search_folders_by_suffix(\".backup\", \"Archive\")\n```\n\n##### `search_folders_by_suffix_recursive(suffix, folder_path=\"\")`\n\nRecursively search for folders with a specific suffix.\n\n**Parameters:**\n- `suffix` (str): Folder suffix\n- `folder_path` (str, optional): Starting folder for search\n\n**Returns:** List of `ItemInfo` objects\n\n**Example:**\n```python\n# Search all .gdb folders recursively\nall_gdb = sp_manager.search_folders_by_suffix_recursive(\".gdb\")\n```\n\n---\n\n### Unified Operations (Auto-detect File/Folder)\n\n#### `download_item(item_path, local_path=None)`\n\nDownload a file or folder (auto-detects type).\n\n**Parameters:**\n- `item_path` (str): Path to item in SharePoint\n- `local_path` (str, optional): Local path to save item\n\n**Returns:**\n- For files: BytesIO or local file path\n- For folders: Path to downloaded folder\n\n**Example:**\n```python\n# Downloads file or folder automatically\nsp_manager.download_item(\"SomeItem\")\n```\n\n#### `delete_item(item_path)`\n\nDelete a file or folder (auto-detects type).\n\n**Parameters:**\n- `item_path` (str): Path to item in SharePoint\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.delete_item(\"OldItem\")\n```\n\n#### `move_item(item_path, destination_folder_path)`\n\nMove a file or folder (auto-detects type).\n\n**Parameters:**\n- `item_path` (str): Current path to item\n- `destination_folder_path` (str): Destination folder path\n\n**Returns:** True if successful\n\n**Example:**\n```python\nsp_manager.move_item(\"SomeItem\", \"Archive\")\n```\n\n---\n\n### Data Classes\n\n#### `ItemInfo`\n\nData class containing item information from SharePoint.\n\n**Attributes:**\n- `name` (str): Item name\n- `path` (str): Full path to item\n- `size` (int): Size in bytes\n- `modified` (str): Last modified datetime (ISO format)\n- `id` (str): SharePoint item ID\n- `webUrl` (str): Web URL to item\n\n**Aliases:** `FileInfo` and `FolderInfo` are aliases for `ItemInfo` (backward compatibility)\n\n**Example:**\n```python\nfiles = sp_manager.search_files_by_suffix(\".csv\")\nfor file in files:\n print(f\"Name: {file.name}\")\n print(f\"Path: {file.path}\")\n print(f\"Size: {file.size} bytes\")\n print(f\"Modified: {file.modified}\")\n print(f\"URL: {file.webUrl}\")\n```\n\n---\n\n## Complete Example\n\n```python\nfrom sharepointer.sharepoint import SharePointManager\nimport os\nfrom dotenv import load_dotenv\n\n# Load configuration\nload_dotenv()\n\n# Initialize\nsp = SharePointManager(\n tenant_id=os.getenv('TENANT_ID'),\n client_id=os.getenv('CLIENT_ID'),\n client_secret=os.getenv('CLIENT_SECRET'),\n site_name=\"contoso\"\n)\n\n# Setup site and drive\nsp.get_site_id(\"/sites/TeamSite\")\nsp.get_drive_id(\"Documents\")\n\n# Search for files\ncsv_files = sp.search_files_by_suffix_recursive(\".csv\", \"Reports\")\nprint(f\"Found {len(csv_files)} CSV files\")\n\n# Download files\nfor file in csv_files:\n content = sp.download_file(file.path)\n print(f\"Downloaded: {file.name}\")\n\n# Upload a file\nsp.upload_file(\"/tmp/report.pdf\", \"Reports/2024\", \"annual_report.pdf\")\n\n# Search for folders\ngdb_folders = sp.search_folders_by_suffix(\".gdb\")\nif gdb_folders:\n # Download first matching folder\n folder = gdb_folders[0]\n sp.download_folder(folder.name, \"/tmp/geodatabase\")\n\n# Move items\nsp.move_item(\"OldData.csv\", \"Archive/2023\")\n\n# Clean up old files\nsp.delete_item(\"TempFolder\")\n```\n\n---\n\n## Error Handling\n\nAll methods raise exceptions on errors. Common exceptions:\n\n- `Exception`: Generic errors (e.g., \"Drive ID not set\")\n- `requests.exceptions.HTTPError`: HTTP errors from Graph API\n - 404: Item not found\n - 401: Authentication failed\n - 403: Permission denied\n\n**Example:**\n```python\ntry:\n sp.download_file(\"nonexistent.txt\")\nexcept requests.exceptions.HTTPError as e:\n if e.response.status_code == 404:\n print(\"File not found\")\n else:\n print(f\"Error: {e.response.status_code}\")\nexcept Exception as e:\n print(f\"General error: {str(e)}\")\n```\n\n---\n\n## Testing\n\nThe library includes comprehensive pytest tests:\n\n```bash\n# Run all tests\npytest tests/\n\n# Run specific test file\npytest tests/test_file_operations.py\n\n# Run with verbose output\npytest tests/ -v\n\n# Run specific test\npytest tests/test_file_operations.py::TestDownloadFile::test_download_file_to_memory\n```\n\n### Test Structure\n\n- `tests/test_authentication_and_init.py` - Authentication and initialization\n- `tests/test_file_operations.py` - File upload, download, delete, move\n- `tests/test_folder_operations.py` - Folder operations and searches\n- `tests/test_search_operations.py` - File search operations\n- `tests/test_dataclasses.py` - ItemInfo dataclass tests\n- `tests/test_url_helpers.py` - URL construction helpers\n- `tests/conftest.py` - Pytest fixtures and configuration\n\n---\n\n## Best Practices\n\n### 1. Always Initialize Properly\n\n```python\n# \u2705 Good\nsp.get_site_id()\nsp.get_drive_id()\nsp.download_file(\"file.txt\")\n\n# \u274c Bad - will raise \"Drive ID not set\" error\nsp.download_file(\"file.txt\")\n```\n\n### 2. Use Suffix Auto-Detection\n\n```python\n# Both work the same\nsp.search_files_by_suffix(\".csv\")\nsp.search_files_by_suffix(\"csv\") # Dot is added automatically\n```\n\n### 3. Use Unified Methods When Type is Unknown\n\n```python\n# Auto-detects if item is file or folder\nsp.download_item(\"UnknownItem\")\nsp.delete_item(\"UnknownItem\")\nsp.move_item(\"UnknownItem\", \"Archive\")\n```\n\n### 4. Handle Paths Consistently\n\n```python\n# Root folder\nsp.search_files_by_suffix(\".csv\", \"\") # Root\nsp.search_files_by_suffix(\".csv\") # Also root (default)\n\n# Nested folders (no leading slash)\nsp.search_files_by_suffix(\".csv\", \"Reports/2024\")\n```\n\n---\n\n## Limitations\n\n1. **Large File Upload**: Files larger than 4MB should use the upload session API (not currently implemented)\n2. **Rate Limiting**: The Microsoft Graph API has rate limits. Consider implementing retry logic for production use\n3. **Concurrent Operations**: No built-in concurrency support. Implement your own if needed\n4. **Permissions**: Requires appropriate SharePoint permissions in Azure AD\n\n---\n\n## Troubleshooting\n\n### Authentication Errors\n\n**Problem:** \"Authentication failed\" error\n\n**Solutions:**\n- Verify Azure AD app credentials\n- Check API permissions are granted and admin consented\n- Ensure client secret hasn't expired\n\n### Drive ID Not Set\n\n**Problem:** \"Drive ID not set\" error\n\n**Solution:**\n```python\nsp.get_drive_id() # Call this before any file/folder operations\n```\n\n### 404 Not Found\n\n**Problem:** Item not found errors\n\n**Solutions:**\n- Verify item path is correct (no leading slash for items)\n- Check item exists in SharePoint\n- Ensure you have permissions to access the item\n\n### Site Name Issues\n\n**Problem:** Can't connect to site\n\n**Solutions:**\n```python\n# If full URL: contoso.sharepoint.com\nsite_name = \"contoso\"\n\n# If full URL: contoso.sharepoint.com/sites/TeamSite\nsite_name = \"contoso\"\nsite_path = \"/sites/TeamSite\"\n```\n\n---\n\n## Changelog\n\n### Version 1.0.0 (Current)\n- Initial release\n- File operations (upload, download, delete, move)\n- Folder operations (download, delete, move)\n- Search operations (files and folders, recursive and non-recursive)\n- Unified operations (auto-detect file/folder type)\n- ItemInfo dataclass with backward compatible aliases\n- Helper method to eliminate code duplication\n- Comprehensive test suite (78 tests)\n\n---\n\n## License\n\n[Add your license here]\n\n---\n\n## Support\n\nFor issues, questions, or contributions, please [add contact information or repository link].\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A tool to manage SharePoint files and folders.",
"version": "0.1.1",
"project_urls": {
"Bug Tracker": "https://github.com/woutervriel/sharepointmanager/issues",
"Homepage": "https://github.com/woutervriel/sharepointmanager",
"Repository": "https://github.com/woutervriel/sharepointmanager.git"
},
"split_keywords": [
"automation",
" file-management",
" office365",
" python",
" sharepoint"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "ae63dc5e3c088d056c34da2990470da880fbb21c684895ee08bcd27003781fe3",
"md5": "2c1cbc1428e45a40ca23a3de5a2250c2",
"sha256": "7fa893b32dbc2fee181abd8f925297e406674065f95282e3cf6777d27f1e2dbe"
},
"downloads": -1,
"filename": "sharepointer-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "2c1cbc1428e45a40ca23a3de5a2250c2",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.13",
"size": 6072,
"upload_time": "2025-11-06T21:12:29",
"upload_time_iso_8601": "2025-11-06T21:12:29.890457Z",
"url": "https://files.pythonhosted.org/packages/ae/63/dc5e3c088d056c34da2990470da880fbb21c684895ee08bcd27003781fe3/sharepointer-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "23cc6fc6da5fd781fc5094b9759ccb3e78fe8df8de33c41ad6ef7f068875f577",
"md5": "7ed148a5031cdcc3b362ac766e9fe73f",
"sha256": "2bcd0b8a5222ce5b3f2aac4f131470a118b295c27b852900e0cdf326cb0741f5"
},
"downloads": -1,
"filename": "sharepointer-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "7ed148a5031cdcc3b362ac766e9fe73f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.13",
"size": 64174,
"upload_time": "2025-11-06T21:12:31",
"upload_time_iso_8601": "2025-11-06T21:12:31.191294Z",
"url": "https://files.pythonhosted.org/packages/23/cc/6fc6da5fd781fc5094b9759ccb3e78fe8df8de33c41ad6ef7f068875f577/sharepointer-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-06 21:12:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "woutervriel",
"github_project": "sharepointmanager",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "sharepointer"
}