Skip to content

sznicolas/pytaskwarrior

Repository files navigation

pytaskwarrior

Python 3.9+ License: MIT Code style: ruff Tests

A modern Python library for TaskWarrior v3.x task management.

No task binary required. pytaskwarrior uses taskchampion3-py-dev — Rust bindings to the taskchampion storage engine — for direct, high-performance SQLite access.

Features

  • No external binary — reads/writes TaskWarrior's SQLite database directly
  • Full CRUD operations — Create, read, update, delete tasks
  • Type-safe — Pydantic models with full type hints
  • Context management — Define, apply, and switch contexts (reads/writes .taskrc)
  • UDA support — User Defined Attributes
  • Virtual tags+OVERDUE, +DUE, +TODAY, +WEEK, +BLOCKED, +READY, and 20+ more
  • Date expressionsdue.before:tomorrow, due.after:eom, compound expressions (now + P3D)
  • Recurring tasks — Full recurrence support
  • Annotations — Add notes to tasks
  • Optional CLI fallback — Pass task_cmd="task" to use the classic CLI adapter

Requirements

  • Python 3.9+
  • taskchampion3-py-dev >= 3.0.1.3 (installed automatically)

The task binary is not required for the default adapter.

Installation

pip install pytaskwarrior

Or install from source:

git clone https://github.com/sznicolas/pytaskwarrior.git
cd pytaskwarrior
uv sync

Quick Start

from taskwarrior import TaskWarrior, TaskInputDTO, Priority

# No binary needed — uses taskchampion SQLite backend directly
tw = TaskWarrior()

# Create a task
task = tw.add_task(TaskInputDTO(
    description="Finish project report",
    priority=Priority.HIGH,
    project="work",
    tags=["urgent"],
    due="friday",
))
print(f"Created: {task.uuid}")

# Query tasks
for t in tw.get_tasks("project:work"):
    print(f"[{t.priority or '-'}] {t.description}")

# Virtual tag filtering
overdue = tw.get_tasks("+OVERDUE")
due_soon = tw.get_tasks("due.before:eow")

# Complete a task
tw.done_task(task.uuid)

Using the CLI adapter (optional)

# Explicit CLI mode — requires the task binary
tw = TaskWarrior(task_cmd="task")

# Custom paths
tw = TaskWarrior(
    taskrc_file="/path/to/.taskrc",
    data_location="/path/to/task/data",
)

Live configuration updates

config_store is the live interface to the taskrc file. Changes made via set_value() or set_sync_config() are immediately effective on the next adapter call — no restart or adapter recreation required.

tw = TaskWarrior()

# Configure remote sync at runtime
tw.config_store.set_value("sync.server.origin", "https://sync.example.com")
tw.config_store.set_value("sync.encryption.secret", "my-passphrase")
tw.synchronize()  # uses the new config immediately

# Or replace the whole sync block at once
tw.config_store.set_sync_config({
    "sync.local.server_dir": "/mnt/shared/taskserver",
})
tw.synchronize()

Note: Changing data_location at runtime is not supported. Create a new TaskWarrior instance if you need a different data directory.

Thread Safety: TaskChampionAdapter enforces thread affinity — it must be used only from the thread that created it. For multi-threaded environments (e.g., FastAPI sync endpoints), create one adapter per thread. For concurrent read access, use AccessMode.ReadOnly. See TaskChampion Adapter docs for details.

Architecture

TaskWarrior (facade)
├── TaskChampionAdapter  ← default: direct SQLite via taskchampion3-py-dev
│   ├── CRUD operations (add/get/modify/delete tasks)
│   ├── Filtering (tc_filter.py — Python reimplementation of TW filters)
│   └── Date resolution (date_resolver.py — Python reimplementation of TW dates)
├── ConfigStore          ← reads/writes ~/.taskrc
│   ├── ContextService   ← define/apply/delete contexts
│   └── UdaService       ← define/delete UDAs
└── TaskWarriorAdapter   ← optional CLI fallback (task_cmd="task")

Supported Filter Syntax

Token Example Description
+tag / -tag +urgent -someday Include / exclude by tag
+VIRTUAL +OVERDUE, +DUE, +TODAY Virtual tags (computed)
status:X status:pending Status filter
project:X project:work Project (hierarchical)
priority:X priority:H Priority
due.before:X due.before:tomorrow Date range
due.after:X due.after:eom Date range
due.by:X due.by:friday Date range (inclusive)

Next Steps

About

Python module wrapping Taskwarrior

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors