Skip to content

Add SDK documentation using mkdocs #188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 22 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,48 @@
# Todoist API Python Client

This is the official Python API client for the Todoist REST API.
This is the official Python SDK for the Todoist API.

### Installation
## Installation

The repository can be included as a dependency in `pyproject.toml`.
It is best to integrate to a release tag to ensure a stable dependency:
```bash
pip install todoist-api-python
```

Or add the project as a dependency in `pyproject.toml`:

```toml
dependencies = [
...
"todoist-api-python>=3.0.0,<4",
...
]
```

### Supported Python Versions

While we only actively test under Python 3.13, we strive to support all versions from Python 3.9 and above.

### Usage
## Usage

An example of initializing the API client and fetching a user's tasks:
Here's an example of initializing the API client, fetching a task, and paginating through its comments:

```python
from todoist_api_python.api_async import TodoistAPIAsync
from todoist_api_python.api import TodoistAPI

# Fetch tasks synchronously
def get_tasks_sync():
api = TodoistAPI("my token")
try:
tasks = api.get_tasks()
print(tasks)
except Exception as error:
print(error)

# Fetch tasks asynchronously
async def get_tasks_async():
api = TodoistAPIAsync("YOURTOKEN")
try:
tasks = await api.get_tasks()
print(tasks)
except Exception as error:
print(error)
```

Example of paginating through a completed project tasks:

```python
def get_all_completed_items(original_params: dict):
params = original_params.copy()
results = []

while True:
response = api.get_completed_items(**(params | {"limit": 100}))
results.append(response.items)

if not response.has_more:
break

params["cursor"] = response.next_cursor
api = TodoistAPI("YOUR_API_TOKEN")

# flatten the results
return [item for sublist in results for item in sublist]
task = api.get_task("6X4Vw2Hfmg73Q2XR")
print(f"Task: {task.content}")

items = get_all_completed_items({"project_id": 123})
comments_iter = api.get_comments(task_id=task.id)
for comments in comments_iter:
for comment in comments:
print(f"Comment: {comment.content}")
```

### Documentation
## Documentation

For more detailed reference documentation, have a look at the [API documentation with Python examples](https://developer.todoist.com/rest/v2/?python).
For more detailed reference documentation, have a look at the [SDK documentation](https://doist.github.io/todoist-api-python/) and the [API documentation](https://developer.todoist.com).

### Development
## Development

To install Python dependencies:

Expand Down Expand Up @@ -104,10 +74,10 @@ A new update is automatically released by GitHub Actions, by creating a release

Users of the API client can then update to the new version in their `pyproject.toml` file.

### Feedback
## Feedback

Any feedback, such as bugs, questions, comments, etc. can be reported as *Issues* in this repository, and will be handled by Doist.
Any feedback, bugs, questions, comments, etc., can be reported as *Issues* in this repository.

### Contributions

We would love contributions in the form of *Pull requests* in this repository.
We would love contributions! *Pull requests* are welcome.
4 changes: 4 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# API Client

::: todoist_api_python.api.TodoistAPI
::: todoist_api_python.api.ResultsPaginator
3 changes: 3 additions & 0 deletions docs/api_async.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# API Client (async)

::: todoist_api_python.api_async.TodoistAPIAsync
Binary file added docs/assets/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions docs/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions docs/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Authentication

This module provides functions to help authenticate with Todoist using the OAuth protocol.

## Quick start

```python
import uuid
from todoist_api_python.authentication import get_access_token, get_authentication_url

# 1. Generate a random state
state = uuid.uuid4()

# 2. Get authorization url
url = get_authentication_url(
client_id="YOUR_CLIENT_ID",
scopes=["data:read", "task:add"],
state=uuid.uuid4()
)

# 3.Redirect user to url
# 4. Handle OAuth callback and get code
code = "CODE_YOU_OBTAINED"

# 5. Exchange code for access token
auth_result = get_access_token(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
code=code,
)

# 6. Ensure state is consistent, and done!
assert(auth_result.state == state)
access_token = auth_result.access_token
```

For detailed implementation steps and security considerations, refer to the [Todoist OAuth documentation](https://todoist.com/api/v1/docs#tag/Authorization/OAuth).

::: todoist_api_python.authentication
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--8<-- "CHANGELOG.md"
45 changes: 45 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Overview

This is the official Python SDK for the Todoist API.

## Installation

```bash
pip install todoist-api-python
```

Or add the project as a dependency in `pyproject.toml`:

```toml
dependencies = [
"todoist-api-python>=3.0.0,<4",
]
```

## Usage

Here's an example of initializing the API client, fetching a task, and paginating through its comments:

```python
from todoist_api_python.api import TodoistAPI

api = TodoistAPI("YOUR_API_TOKEN")

task = api.get_task("6X4Vw2Hfmg73Q2XR")
print(f"Task: {task.content}")

comments_iter = api.get_comments(task_id=task.id)
for comments in comments_iter:
for comment in comments:
print(f"Comment: {comment.content}")
```

## Quick start

- [Authentication](authentication.md)
- [API client](api.md)
- [Models](models.md)

## API reference

For detailed reference documentation, have a look at the [API documentation](https://developer.todoist.com/).
7 changes: 7 additions & 0 deletions docs/models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Models

::: todoist_api_python.models
options:
show_if_no_docstring: true
show_labels: false
members_order: alphabetical
57 changes: 57 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
site_name: "Todoist Python SDK"

repo_url: https://github.com/Doist/todoist-api-python/

theme:
name: material
logo: assets/logo.svg
favicon: assets/favicon.ico
palette:
- media: "(prefers-color-scheme)"
primary: red
toggle:
icon: material/brightness-4
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: black
toggle:
icon: material/brightness-7
name: Switch to light mode
- media: "(prefers-color-scheme: light)"
scheme: default
primary: red
toggle:
icon: material/brightness-auto
name: Follow system preference
features:
- navigation.footer

extra:
social:
- icon: fontawesome/brands/x-twitter
link: https://x.com/doistdevs
- icon: fontawesome/brands/github
link: https://github.com/doist

markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences

plugins:
- search
- mkdocstrings:
handlers:
python:
options:
docstring_style: sphinx
backlinks: true
filters: ["!^_"]
show_symbol_type_toc: True
signature_crossrefs: true
unwrap_annotated: true
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ dev = [
"ruff>=0.11.0,<0.12",
]

docs = [
"mkdocs>=1.6.1,<2.0.0",
"mkdocstrings[python]>=0.29.1,<1.0.0",
"mkdocs-material>=9.6.11,<10.0.0",
]

[tool.hatch.build.targets.sdist]
include = ["LICENSE"]

Expand Down
23 changes: 21 additions & 2 deletions todoist_api_python/authentication.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Any
from typing import Any, Literal
from urllib.parse import urlencode

import requests
Expand All @@ -17,8 +17,27 @@
from todoist_api_python._core.utils import run_async
from todoist_api_python.models import AuthResult

# task:add - Only create new tasks
# data:read - Read-only access
# data:read_write - Read and write access
# data:delete - Full access including delete
# project:delete - Can delete projects

def get_authentication_url(client_id: str, scopes: list[str], state: str) -> str:
"""
Possible permission scopes:

- data:read: Read-only access
- data:read_write: Read and write access
- data:delete: Full access including delete
- task:add: Can create new tasks
- project:delete: Can delete projects
"""
Scope = Literal[
"task:add", "data:read", "data:read_write", "data:delete", "project:delete"
]


def get_authentication_url(client_id: str, scopes: list[Scope], state: str) -> str:
"""Get authorization URL to initiate OAuth flow."""
if len(scopes) == 0:
raise ValueError("At least one authorization scope should be requested.")
Expand Down
Loading