Metadata-Version: 2.5
Name: piazza-sdk
Version: 2026.8.24
Summary: Modern async Python SDK for Piazza's internal API (2026+)
Project-URL: Homepage, https://github.com/ayushmorbar/piazza-sdk
Project-URL: Documentation, https://ayushmorbar.github.io/piazza-sdk/
Project-URL: Repository, https://github.com/ayushmorbar/piazza-sdk
Project-URL: Issues, https://github.com/ayushmorbar/piazza-sdk/issues
Project-URL: Changelog, https://github.com/ayushmorbar/piazza-sdk/blob/main/CHANGELOG.md
Author-email: Ayush Morbar <ayushmorbar@users.noreply.github.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: api,async,education,piazza,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Education
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Requires-Dist: cryptography>=43.0.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic-settings>=2.3.0
Requires-Dist: pydantic>=2.8.0
Requires-Dist: tenacity>=8.3.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10.0; extra == 'dev'
Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
Requires-Dist: pytest>=8.2.0; extra == 'dev'
Requires-Dist: ruff>=0.5.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
Requires-Dist: mkdocs>=1.5.0; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.26.0; extra == 'docs'
Provides-Extra: normalization
Requires-Dist: html2text>=2024.2.26; extra == 'normalization'
Description-Content-Type: text/markdown

# Piazza SDK

> Modern async Python SDK for Piazza's internal API.

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-green.svg)](LICENSE)
[![CI](https://img.shields.io/badge/CI-passing-brightgreen.svg)](https://github.com/ayushmorbar/piazza-sdk/actions)

**Disclaimer:** Piazza SDK is an unofficial, community-driven open-source project. It is not affiliated with, endorsed by, or associated with Piazza Technologies, Inc. "Piazza" is a registered trademark of Piazza Technologies, Inc.

## Features

- **Async/await** throughout with `httpx`
- **Pydantic v2** models with dot-notation access
- **Type hints** and PEP 561 `py.typed` marker
- **Feed operations** — get, filter (unread, following, folder), search
- **Post lifecycle** — create, read, update, delete, follow-ups, answers, replies
- **User management** — profiles, classes, permissions
- **Rate limiting** with automatic retry and exponential backoff
- **Comprehensive exception hierarchy** for fine-grained error handling

## Installation

```bash
pip install piazza-sdk
```

## Quick Start

We have provided a set of comprehensive, runnable tutorials in the `docs/_tutorials/` directory. These scripts are fully documented and show exactly how to use the SDK.

- [01: Getting Started](docs/_tutorials/01_getting_started.py) — Authentication and basic profiles
- [02: Reading the Feed](docs/_tutorials/02_reading_the_feed.py) — Fetching posts and using filters
- [03: Creating & Answering](docs/_tutorials/03_creating_and_answering_posts.py) — Interacting with posts
- [04: Advanced User Stats](docs/_tutorials/04_advanced_user_stats.py) — Course analytics and online users

Here is a quick snippet to fetch your class feed:

```python
import asyncio
from piazza_sdk import Piazza, SessionConfig, SessionStateManager

async def main():
    config = SessionConfig(user_agent="my-app/1.0")
    async with SessionStateManager(config) as session:
        await session.login(email="your@email.com", password="your_password")
        
        piazza = Piazza(session)
        classes = await piazza.get_user_classes()
        network = piazza.network(classes[0]["nid"])

        feed = await network.get_feed(limit=5)
        for item in feed.feed:
            print(f"{item.subject} ({item.type})")

if __name__ == "__main__":
    asyncio.run(main())
```

## Acknowledgements

Piazza SDK is deeply grateful to the open-source community. Special thanks to [HfPiazza](https://github.com/hfaran/piazza-api) for earlier inspirations in navigating the complex Piazza undocumented API layer.

## API

### Core Classes

| Class                | Description                                  |
| -------------------- | -------------------------------------------- |
| `Piazza`             | Entry point — user profile, classes          |
| `Network`            | Per-class operations — feed, posts, users    |
| `SessionConfig`      | Configuration (course ID, timeouts, retries) |
| `SessionStateManager`| Async context manager — session lifecycle    |

### Models

All models support dot-notation access:

```python
post = await network.get_post(cid)
post.id          # str
post.subject     # str
post.type        # PostType
post.created     # datetime
post.user_name   # str
post.tags        # list[str]

# On-demand HTML-to-Markdown normalization
normalized = post.normalized()
print(normalized.subject)  # Clean Markdown text
```

### Filters

```python
from piazza_sdk import UnreadFilter, FollowingFilter, FolderFilter

feed = await network.get_filtered_feed(UnreadFilter())
feed = await network.get_filtered_feed(FolderFilter("homework"))
```

### Error Handling

```python
from piazza_sdk import AuthenticationError, RateLimitError, PiazzaSDKError

try:
    await network.get_post("invalid")
except AuthenticationError:
    print("Check your credentials")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after_ms}ms")
except PiazzaSDKError as e:
    print(f"SDK error: {e}")
```

### Domain Modules (Advanced)

For hexagonal architecture or standalone use, the `domain` package provides async functions that operate directly on RPC/session objects:

```python
from piazza_sdk.domain import get_feed, create_post, search

feed = await get_feed(rpc, network_id="abc123", limit=10)
post = await create_post(rpc, network_id="abc123", subject="Question", content="...")
results = await search(rpc, network_id="abc123", query="homework")
```

## Development

```bash
git clone https://github.com/ayushmorbar/piazza-sdk.git
cd piazza-sdk
pip install -e ".[dev]"

# Lint
ruff check src/ tests/

# Format
ruff format src/ tests/

# Type check
mypy src/

# Test
pytest
```

## License

Apache 2.0 — see [LICENSE](LICENSE) for details.
