pytaskwarrior 2.0.7__tar.gz → 3.0.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/PKG-INFO +2 -1
  2. pytaskwarrior-3.0.0a2/README.md +123 -0
  3. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/pyproject.toml +19 -1
  4. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/pytaskwarrior.egg-info/PKG-INFO +2 -1
  5. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/pytaskwarrior.egg-info/SOURCES.txt +5 -0
  6. pytaskwarrior-3.0.0a2/src/pytaskwarrior.egg-info/requires.txt +2 -0
  7. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/__init__.py +3 -4
  8. pytaskwarrior-3.0.0a2/src/taskwarrior/adapters/__init__.py +79 -0
  9. pytaskwarrior-3.0.0a2/src/taskwarrior/adapters/taskchampion_adapter.py +643 -0
  10. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/adapters/taskwarrior_adapter.py +51 -91
  11. pytaskwarrior-3.0.0a2/src/taskwarrior/adapters/tc_converter.py +314 -0
  12. pytaskwarrior-3.0.0a2/src/taskwarrior/adapters/tc_filter.py +445 -0
  13. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/config/config_store.py +84 -0
  14. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/dto/task_id.py +5 -0
  15. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/main.py +177 -69
  16. pytaskwarrior-3.0.0a2/src/taskwarrior/services/context_service.py +89 -0
  17. pytaskwarrior-3.0.0a2/src/taskwarrior/services/uda_service.py +111 -0
  18. pytaskwarrior-3.0.0a2/src/taskwarrior/utils/date_resolver.py +252 -0
  19. pytaskwarrior-3.0.0a2/src/taskwarrior/utils/virtual_tags.py +40 -0
  20. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/tests/test_main_get_udas.py +2 -2
  21. pytaskwarrior-2.0.7/README.md +0 -385
  22. pytaskwarrior-2.0.7/src/pytaskwarrior.egg-info/requires.txt +0 -1
  23. pytaskwarrior-2.0.7/src/taskwarrior/adapters/__init__.py +0 -1
  24. pytaskwarrior-2.0.7/src/taskwarrior/services/context_service.py +0 -174
  25. pytaskwarrior-2.0.7/src/taskwarrior/services/uda_service.py +0 -143
  26. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/LICENSE +0 -0
  27. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/PYPI_README.md +0 -0
  28. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/setup.cfg +0 -0
  29. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/__init__.py +0 -0
  30. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/pytaskwarrior.egg-info/dependency_links.txt +0 -0
  31. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/pytaskwarrior.egg-info/top_level.txt +0 -0
  32. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/config/uda_parser.py +0 -0
  33. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/dto/__init__.py +0 -0
  34. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/dto/annotation_dto.py +0 -0
  35. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/dto/context_dto.py +0 -0
  36. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/dto/task_dto.py +0 -0
  37. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/dto/uda_dto.py +0 -0
  38. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/enums.py +0 -0
  39. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/exceptions.py +0 -0
  40. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/py.typed +0 -0
  41. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/registry/__init__.py +0 -0
  42. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/registry/uda_registry.py +0 -0
  43. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/services/__init__.py +0 -0
  44. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/utils/__init__.py +0 -0
  45. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/utils/conversions.py +0 -0
  46. {pytaskwarrior-2.0.7 → pytaskwarrior-3.0.0a2}/src/taskwarrior/utils/dto_converter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytaskwarrior
3
- Version: 2.0.7
3
+ Version: 3.0.0a2
4
4
  Summary: Taskwarrior wrapper python module
5
5
  Author-email: sznicolas <sznicolas@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -21,6 +21,7 @@ Requires-Python: >=3.12
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: pydantic>=2.11.7
24
+ Requires-Dist: taskchampion3-py-dev>=3.0.1.1a0
24
25
  Dynamic: license-file
25
26
 
26
27
  # pytaskwarrior
@@ -0,0 +1,123 @@
1
+ # pytaskwarrior
2
+
3
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
6
+ [![Tests](https://github.com/sznicolas/pytaskwarrior/workflows/CI/badge.svg)](https://github.com/sznicolas/pytaskwarrior/actions)
7
+
8
+ A modern Python library for [TaskWarrior](https://taskwarrior.org/) v3.x task management.
9
+
10
+ **No `task` binary required.** pytaskwarrior uses
11
+ [taskchampion-py](https://github.com/GothenburgBitFactory/taskchampion-py) — Rust bindings
12
+ to the taskchampion storage engine — for direct, high-performance SQLite access.
13
+
14
+ ## Features
15
+
16
+ - **No external binary** — reads/writes TaskWarrior's SQLite database directly
17
+ - **Full CRUD operations** — Create, read, update, delete tasks
18
+ - **Type-safe** — Pydantic models with full type hints
19
+ - **Context management** — Define, apply, and switch contexts (reads/writes `.taskrc`)
20
+ - **UDA support** — User Defined Attributes
21
+ - **Virtual tags** — `+OVERDUE`, `+DUE`, `+TODAY`, `+WEEK`, `+BLOCKED`, `+READY`, and 20+ more
22
+ - **Date expressions** — `due.before:tomorrow`, `due.after:eom`, compound expressions (`now + P3D`)
23
+ - **Recurring tasks** — Full recurrence support
24
+ - **Annotations** — Add notes to tasks
25
+ - **Optional CLI fallback** — Pass `task_cmd="task"` to use the classic CLI adapter
26
+
27
+ ## Requirements
28
+
29
+ - Python 3.12+
30
+ - `taskchampion-py` >= 3.0.1.1 (installed automatically)
31
+
32
+ The `task` binary is **not required** for the default adapter.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install pytaskwarrior
38
+ ```
39
+
40
+ Or install from source:
41
+
42
+ ```bash
43
+ git clone https://github.com/sznicolas/pytaskwarrior.git
44
+ cd pytaskwarrior
45
+ uv sync
46
+ ```
47
+
48
+ ## Quick Start
49
+
50
+ ```python
51
+ from taskwarrior import TaskWarrior, TaskInputDTO, Priority
52
+
53
+ # No binary needed — uses taskchampion SQLite backend directly
54
+ tw = TaskWarrior()
55
+
56
+ # Create a task
57
+ task = tw.add_task(TaskInputDTO(
58
+ description="Finish project report",
59
+ priority=Priority.HIGH,
60
+ project="work",
61
+ tags=["urgent"],
62
+ due="friday",
63
+ ))
64
+ print(f"Created: {task.uuid}")
65
+
66
+ # Query tasks
67
+ for t in tw.get_tasks("project:work"):
68
+ print(f"[{t.priority or '-'}] {t.description}")
69
+
70
+ # Virtual tag filtering
71
+ overdue = tw.get_tasks("+OVERDUE")
72
+ due_soon = tw.get_tasks("due.before:eow")
73
+
74
+ # Complete a task
75
+ tw.done_task(task.uuid)
76
+ ```
77
+
78
+ ### Using the CLI adapter (optional)
79
+
80
+ ```python
81
+ # Explicit CLI mode — requires the task binary
82
+ tw = TaskWarrior(task_cmd="task")
83
+
84
+ # Custom paths
85
+ tw = TaskWarrior(
86
+ taskrc_file="/path/to/.taskrc",
87
+ data_location="/path/to/task/data",
88
+ )
89
+ ```
90
+
91
+ ## Architecture
92
+
93
+ ```
94
+ TaskWarrior (facade)
95
+ ├── TaskChampionAdapter ← default: direct SQLite via taskchampion-py
96
+ │ ├── CRUD operations (add/get/modify/delete tasks)
97
+ │ ├── Filtering (tc_filter.py — Python reimplementation of TW filters)
98
+ │ └── Date resolution (date_resolver.py — Python reimplementation of TW dates)
99
+ ├── ConfigStore ← reads/writes ~/.taskrc
100
+ │ ├── ContextService ← define/apply/delete contexts
101
+ │ └── UdaService ← define/delete UDAs
102
+ └── TaskWarriorAdapter ← optional CLI fallback (task_cmd="task")
103
+ ```
104
+
105
+ ## Supported Filter Syntax
106
+
107
+ | Token | Example | Description |
108
+ |-------|---------|-------------|
109
+ | `+tag` / `-tag` | `+urgent -someday` | Include / exclude by tag |
110
+ | `+VIRTUAL` | `+OVERDUE`, `+DUE`, `+TODAY` | Virtual tags (computed) |
111
+ | `status:X` | `status:pending` | Status filter |
112
+ | `project:X` | `project:work` | Project (hierarchical) |
113
+ | `priority:X` | `priority:H` | Priority |
114
+ | `due.before:X` | `due.before:tomorrow` | Date range |
115
+ | `due.after:X` | `due.after:eom` | Date range |
116
+ | `due.by:X` | `due.by:friday` | Date range (inclusive) |
117
+
118
+ ## Next Steps
119
+
120
+ - [Getting Started](docs/getting-started.md) — Installation and setup
121
+ - [TaskChampion Adapter](docs/taskchampion-adapter.md) — Technical details
122
+ - [API Reference](docs/api/taskwarrior.md) — Full API documentation
123
+ - [Examples](docs/examples/basic.md) — More usage examples
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pytaskwarrior"
3
- version = "2.0.7"
3
+ version = "3.0.0a2"
4
4
  description = "Taskwarrior wrapper python module"
5
5
  readme = "PYPI_README.md"
6
6
  requires-python = ">=3.12"
@@ -21,6 +21,8 @@ classifiers = [
21
21
  ]
22
22
  dependencies = [
23
23
  "pydantic>=2.11.7",
24
+ # "taskchampion-py-dev>=3.0.1.1a1"
25
+ "taskchampion3-py-dev>=3.0.1.1a0",
24
26
  ]
25
27
 
26
28
  [dependency-groups]
@@ -94,3 +96,19 @@ strict_equality = true
94
96
  [[tool.mypy.overrides]]
95
97
  module = "tests.*"
96
98
  disallow_untyped_defs = false
99
+
100
+ [tool.pytest.ini_options]
101
+ testpaths = ["tests"]
102
+ addopts = "--tb=short -q --ignore=tests/integration"
103
+
104
+ [tool.coverage.run]
105
+ source = ["src/taskwarrior"]
106
+ branch = true
107
+
108
+ [tool.coverage.report]
109
+ show_missing = true
110
+ exclude_lines = [
111
+ "pragma: no cover",
112
+ "if TYPE_CHECKING:",
113
+ "raise NotImplementedError",
114
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytaskwarrior
3
- Version: 2.0.7
3
+ Version: 3.0.0a2
4
4
  Summary: Taskwarrior wrapper python module
5
5
  Author-email: sznicolas <sznicolas@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -21,6 +21,7 @@ Requires-Python: >=3.12
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: pydantic>=2.11.7
24
+ Requires-Dist: taskchampion3-py-dev>=3.0.1.1a0
24
25
  Dynamic: license-file
25
26
 
26
27
  # pytaskwarrior
@@ -14,7 +14,10 @@ src/taskwarrior/exceptions.py
14
14
  src/taskwarrior/main.py
15
15
  src/taskwarrior/py.typed
16
16
  src/taskwarrior/adapters/__init__.py
17
+ src/taskwarrior/adapters/taskchampion_adapter.py
17
18
  src/taskwarrior/adapters/taskwarrior_adapter.py
19
+ src/taskwarrior/adapters/tc_converter.py
20
+ src/taskwarrior/adapters/tc_filter.py
18
21
  src/taskwarrior/config/config_store.py
19
22
  src/taskwarrior/config/uda_parser.py
20
23
  src/taskwarrior/dto/__init__.py
@@ -30,5 +33,7 @@ src/taskwarrior/services/context_service.py
30
33
  src/taskwarrior/services/uda_service.py
31
34
  src/taskwarrior/utils/__init__.py
32
35
  src/taskwarrior/utils/conversions.py
36
+ src/taskwarrior/utils/date_resolver.py
33
37
  src/taskwarrior/utils/dto_converter.py
38
+ src/taskwarrior/utils/virtual_tags.py
34
39
  tests/test_main_get_udas.py
@@ -0,0 +1,2 @@
1
+ pydantic>=2.11.7
2
+ taskchampion3-py-dev>=3.0.1.1a0
@@ -29,7 +29,9 @@ Example:
29
29
 
30
30
  Requirements:
31
31
  - Python 3.12+
32
- - TaskWarrior 3.4+ installed and accessible via `task` command
32
+ - The ``task`` binary is only required when passing ``task_cmd`` to
33
+ :class:`~taskwarrior.TaskWarrior`; the default backend (TaskChampion)
34
+ works without it.
33
35
  """
34
36
 
35
37
  import importlib.metadata
@@ -53,13 +55,10 @@ from .registry.uda_registry import UdaRegistry
53
55
  from .utils.dto_converter import task_output_to_input
54
56
 
55
57
  __version__ = importlib.metadata.version("pytaskwarrior")
56
- # Backwards-compatible alias
57
- version = __version__
58
58
 
59
59
  __all__ = [
60
60
  "AnnotationDTO",
61
61
  "ContextDTO",
62
- "version",
63
62
  "Priority",
64
63
  "RecurrencePeriod",
65
64
  "TaskID",
@@ -0,0 +1,79 @@
1
+ """Adapter interfaces for pytaskwarrior.
2
+
3
+ This module exposes the :class:`AdapterProtocol` that any backend adapter
4
+ must satisfy, enabling dependency injection and testability.
5
+
6
+ It also re-exports :class:`~taskchampion.AccessMode` for convenience, so
7
+ callers do not need to import directly from ``taskchampion``.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Protocol, runtime_checkable
13
+
14
+ from taskchampion import AccessMode
15
+
16
+ from ..dto.task_dto import TaskInputDTO, TaskOutputDTO
17
+ from ..dto.task_id import TaskRef
18
+
19
+ __all__ = ["AdapterProtocol", "AccessMode"]
20
+
21
+
22
+ @runtime_checkable
23
+ class AdapterProtocol(Protocol):
24
+ """Structural protocol satisfied by every pytaskwarrior adapter.
25
+
26
+ Both :class:`~taskwarrior.adapters.taskwarrior_adapter.TaskWarriorAdapter`
27
+ (subprocess-based) and
28
+ :class:`~taskwarrior.adapters.taskchampion_adapter.TaskChampionAdapter`
29
+ (direct SQLite via taskchampion-py) implement this protocol.
30
+ """
31
+
32
+ def add_task(self, task: TaskInputDTO) -> TaskOutputDTO: ...
33
+
34
+ def modify_task(self, task: TaskInputDTO, task_id: TaskRef) -> TaskOutputDTO: ...
35
+
36
+ def get_task(self, task_id: TaskRef) -> TaskOutputDTO: ...
37
+
38
+ def get_tasks(
39
+ self,
40
+ filter: str = "",
41
+ include_completed: bool = False,
42
+ include_deleted: bool = False,
43
+ ) -> list[TaskOutputDTO]: ...
44
+
45
+ def get_recurring_task(self, task_id: TaskRef) -> TaskOutputDTO: ...
46
+
47
+ def get_recurring_instances(self, task_id: TaskRef) -> list[TaskOutputDTO]: ...
48
+
49
+ def delete_task(self, task_id: TaskRef) -> None: ...
50
+
51
+ def purge_task(self, task_id: TaskRef) -> None: ...
52
+
53
+ def done_task(self, task_id: TaskRef) -> None: ...
54
+
55
+ def start_task(self, task_id: TaskRef) -> None: ...
56
+
57
+ def stop_task(self, task_id: TaskRef) -> None: ...
58
+
59
+ def annotate_task(self, task_id: TaskRef, annotation: str) -> None: ...
60
+
61
+ def synchronize(self) -> None: ...
62
+
63
+ def is_sync_configured(self) -> bool: ...
64
+
65
+ def has_local_changes(self) -> bool: ...
66
+
67
+ def pending_local_ops_count(self) -> int: ...
68
+
69
+ def task_calc(self, date_str: str) -> str: ...
70
+
71
+ def task_date_validator(self, date_str: str) -> bool: ...
72
+
73
+ def get_data_location(self) -> str | None: ...
74
+
75
+ def get_version(self) -> str: ...
76
+
77
+ def get_projects(self) -> list[str]: ...
78
+
79
+ def get_tags(self, include_virtual_tags: bool = False) -> list[str]: ...