pytaskwarrior 1.1.0__tar.gz → 1.2.0.dev0__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.
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/PKG-INFO +3 -2
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/PYPI_README.md +1 -1
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/README.md +1 -1
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/pyproject.toml +2 -1
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/PKG-INFO +3 -2
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/SOURCES.txt +2 -0
- pytaskwarrior-1.2.0.dev0/src/pytaskwarrior.egg-info/requires.txt +2 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/adapters/taskwarrior_adapter.py +57 -2
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/exceptions.py +8 -0
- pytaskwarrior-1.2.0.dev0/src/taskwarrior/protocols/sync.py +8 -0
- pytaskwarrior-1.2.0.dev0/src/taskwarrior/sync_backends/sync_local.py +14 -0
- pytaskwarrior-1.1.0/src/pytaskwarrior.egg-info/requires.txt +0 -1
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/LICENSE +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/setup.cfg +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/dependency_links.txt +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/top_level.txt +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/adapters/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/annotation_dto.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/context_dto.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/task_dto.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/uda_dto.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/enums.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/main.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/py.typed +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/registry/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/registry/uda_registry.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/context_service.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/uda_service.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/utils/__init__.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/utils/conversions.py +0 -0
- {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/utils/dto_converter.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytaskwarrior
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0.dev0
|
|
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: taskchampion-py>=2.0.2
|
|
24
25
|
Dynamic: license-file
|
|
25
26
|
|
|
26
27
|
# pytaskwarrior
|
|
@@ -34,7 +35,7 @@ Dynamic: license-file
|
|
|
34
35
|
|
|
35
36
|
A modern Python wrapper for [TaskWarrior](https://taskwarrior.org/), the command-line task management tool.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
Production-ready with 132 tests (96% coverage), strict type checking, and professional-grade code quality. Zero linting errors, full async-safe subprocess handling, and PEP 561 type hints for IDE support.
|
|
38
39
|
|
|
39
40
|
## Features
|
|
40
41
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
A modern Python wrapper for [TaskWarrior](https://taskwarrior.org/), the command-line task management tool.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Production-ready with 132 tests (96% coverage), strict type checking, and professional-grade code quality. Zero linting errors, full async-safe subprocess handling, and PEP 561 type hints for IDE support.
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
A modern Python wrapper for [TaskWarrior](https://taskwarrior.org/) v3.4, the command-line task management tool.
|
|
11
11
|
|
|
12
|
-
**v1.
|
|
12
|
+
**v1.1.1**: Production-ready with 132 tests (96% coverage), strict type checking, and professional-grade code quality. Zero linting errors, full async-safe subprocess handling, and PEP 561 type hints for IDE support.
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pytaskwarrior"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.2.0.dev0"
|
|
4
4
|
description = "Taskwarrior wrapper python module"
|
|
5
5
|
readme = "PYPI_README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -21,6 +21,7 @@ classifiers = [
|
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
23
|
"pydantic>=2.11.7",
|
|
24
|
+
"taskchampion-py>=2.0.2",
|
|
24
25
|
]
|
|
25
26
|
|
|
26
27
|
[dependency-groups]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytaskwarrior
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0.dev0
|
|
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: taskchampion-py>=2.0.2
|
|
24
25
|
Dynamic: license-file
|
|
25
26
|
|
|
26
27
|
# pytaskwarrior
|
|
@@ -34,7 +35,7 @@ Dynamic: license-file
|
|
|
34
35
|
|
|
35
36
|
A modern Python wrapper for [TaskWarrior](https://taskwarrior.org/), the command-line task management tool.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
Production-ready with 132 tests (96% coverage), strict type checking, and professional-grade code quality. Zero linting errors, full async-safe subprocess handling, and PEP 561 type hints for IDE support.
|
|
38
39
|
|
|
39
40
|
## Features
|
|
40
41
|
|
|
@@ -20,11 +20,13 @@ src/taskwarrior/dto/annotation_dto.py
|
|
|
20
20
|
src/taskwarrior/dto/context_dto.py
|
|
21
21
|
src/taskwarrior/dto/task_dto.py
|
|
22
22
|
src/taskwarrior/dto/uda_dto.py
|
|
23
|
+
src/taskwarrior/protocols/sync.py
|
|
23
24
|
src/taskwarrior/registry/__init__.py
|
|
24
25
|
src/taskwarrior/registry/uda_registry.py
|
|
25
26
|
src/taskwarrior/services/__init__.py
|
|
26
27
|
src/taskwarrior/services/context_service.py
|
|
27
28
|
src/taskwarrior/services/uda_service.py
|
|
29
|
+
src/taskwarrior/sync_backends/sync_local.py
|
|
28
30
|
src/taskwarrior/utils/__init__.py
|
|
29
31
|
src/taskwarrior/utils/conversions.py
|
|
30
32
|
src/taskwarrior/utils/dto_converter.py
|
{pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/adapters/taskwarrior_adapter.py
RENAMED
|
@@ -16,7 +16,7 @@ from uuid import UUID
|
|
|
16
16
|
|
|
17
17
|
from ..dto.task_dto import TaskInputDTO, TaskOutputDTO
|
|
18
18
|
from ..enums import TaskStatus
|
|
19
|
-
from ..exceptions import TaskNotFound, TaskValidationError, TaskWarriorError
|
|
19
|
+
from ..exceptions import TaskNotFound, TaskValidationError, TaskWarriorError, TaskSyncError
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
22
22
|
|
|
@@ -35,7 +35,13 @@ class TaskWarriorInfo(TypedDict, total=False):
|
|
|
35
35
|
version: str
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
from ..protocols.sync import SyncProtocol
|
|
39
|
+
from ..sync_backends.sync_local import SyncLocal
|
|
40
|
+
from typing import Optional
|
|
41
|
+
|
|
38
42
|
class TaskWarriorAdapter:
|
|
43
|
+
_sync_configured: bool | None = None
|
|
44
|
+
|
|
39
45
|
"""Low-level adapter for TaskWarrior CLI commands.
|
|
40
46
|
|
|
41
47
|
This class handles direct communication with the TaskWarrior binary,
|
|
@@ -52,7 +58,8 @@ class TaskWarriorAdapter:
|
|
|
52
58
|
self,
|
|
53
59
|
task_cmd: str = "task",
|
|
54
60
|
taskrc_file: str = "~/.taskrc",
|
|
55
|
-
data_location: str | None = None
|
|
61
|
+
data_location: str | None = None,
|
|
62
|
+
sync: SyncProtocol | None = None
|
|
56
63
|
):
|
|
57
64
|
"""Initialize the adapter.
|
|
58
65
|
|
|
@@ -60,6 +67,7 @@ class TaskWarriorAdapter:
|
|
|
60
67
|
task_cmd: TaskWarrior binary name or path.
|
|
61
68
|
taskrc_file: Path to taskrc file.
|
|
62
69
|
data_location: Path to data directory (optional).
|
|
70
|
+
sync: Optional SyncProtocol instance to handle synchronization.
|
|
63
71
|
|
|
64
72
|
Raises:
|
|
65
73
|
TaskValidationError: If TaskWarrior binary not found.
|
|
@@ -77,6 +85,37 @@ class TaskWarriorAdapter:
|
|
|
77
85
|
|
|
78
86
|
self._options.extend(DEFAULT_OPTIONS)
|
|
79
87
|
|
|
88
|
+
# --- Begin sync config parsing ---
|
|
89
|
+
self.sync_config = self._parse_sync_config()
|
|
90
|
+
# --- End sync config parsing ---
|
|
91
|
+
|
|
92
|
+
# SyncProtocol injection or auto-detection
|
|
93
|
+
if sync is not None:
|
|
94
|
+
self._sync = sync
|
|
95
|
+
elif self.sync_config.get('sync.local.server_dir'):
|
|
96
|
+
# Prepare SyncLocal if sync.local.server is present
|
|
97
|
+
try:
|
|
98
|
+
self._sync = SyncLocal(self.sync_config.get('sync.local.server_dir'))
|
|
99
|
+
except Exception:
|
|
100
|
+
self._sync = None
|
|
101
|
+
else:
|
|
102
|
+
self._sync = None
|
|
103
|
+
|
|
104
|
+
def _parse_sync_config(self) -> dict:
|
|
105
|
+
"""Parse the taskrc file and return all keys starting with 'sync.'"""
|
|
106
|
+
config = {}
|
|
107
|
+
if self.taskrc_file.exists():
|
|
108
|
+
with self.taskrc_file.open("r") as f:
|
|
109
|
+
for line in f:
|
|
110
|
+
line = line.strip()
|
|
111
|
+
if not line or line.startswith('#'):
|
|
112
|
+
continue
|
|
113
|
+
if line.startswith('sync.'):
|
|
114
|
+
if '=' in line:
|
|
115
|
+
key, value = line.split('=', 1)
|
|
116
|
+
config[key.strip()] = value.strip()
|
|
117
|
+
return config
|
|
118
|
+
|
|
80
119
|
def _check_binary_path(self, task_cmd: str) -> Path:
|
|
81
120
|
"""Verify TaskWarrior binary exists in PATH."""
|
|
82
121
|
resolved_path = shutil.which(task_cmd)
|
|
@@ -104,6 +143,12 @@ rc.bulk=0
|
|
|
104
143
|
self.data_location.mkdir(parents=True, exist_ok=True)
|
|
105
144
|
logger.info(f"Created Task data direcory '{self.data_location}'")
|
|
106
145
|
|
|
146
|
+
def is_sync_configured(self) -> bool:
|
|
147
|
+
"""Return True if synchronization is configured via SyncProtocol."""
|
|
148
|
+
if self._sync is None:
|
|
149
|
+
return False
|
|
150
|
+
return True
|
|
151
|
+
|
|
107
152
|
def run_task_command(
|
|
108
153
|
self, args: list[str], no_opt: bool = False
|
|
109
154
|
) -> subprocess.CompletedProcess[str]:
|
|
@@ -146,6 +191,16 @@ rc.bulk=0
|
|
|
146
191
|
logger.error(f"Exception while running '{cmd}': {e}")
|
|
147
192
|
raise
|
|
148
193
|
|
|
194
|
+
def synchronize(self) -> None:
|
|
195
|
+
"""Synchronize tasks using the injected or auto-detected SyncProtocol."""
|
|
196
|
+
if self._sync is not None:
|
|
197
|
+
try:
|
|
198
|
+
self._sync.synchronize()
|
|
199
|
+
except Exception as e:
|
|
200
|
+
raise TaskSyncError(f"SyncProtocol synchronization failed: {e}")
|
|
201
|
+
else:
|
|
202
|
+
raise TaskSyncError("No SyncProtocol is configured for synchronization.")
|
|
203
|
+
|
|
149
204
|
@staticmethod
|
|
150
205
|
def _wrap_filter(f: str) -> str:
|
|
151
206
|
"""Wrap a non-empty filter expression in parentheses.
|
|
@@ -21,6 +21,14 @@ class TaskWarriorError(Exception):
|
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class TaskSyncError(TaskWarriorError):
|
|
25
|
+
"""Raised when a synchronization error occurs in TaskWarrior.
|
|
26
|
+
|
|
27
|
+
This exception is used to signal errors encountered during sync operations.
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
24
32
|
class TaskNotFound(TaskWarriorError): # noqa: N818
|
|
25
33
|
"""Raised when a requested task does not exist.
|
|
26
34
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from ..protocols.sync import SyncProtocol
|
|
3
|
+
|
|
4
|
+
from taskchampion import Replica
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
class SyncLocal(SyncProtocol):
|
|
8
|
+
def __init__(self, sync_dir: str):
|
|
9
|
+
self.sync_dir = sync_dir
|
|
10
|
+
self._replica = Replica.new_on_disk(self.sync_dir, True)
|
|
11
|
+
|
|
12
|
+
def synchronize(self) -> None:
|
|
13
|
+
# Use the Replica object for local sync
|
|
14
|
+
self._replica.sync_to_local(self.sync_dir, avoid_snapshots=False)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pydantic>=2.11.7
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/context_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|