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.
Files changed (35) hide show
  1. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/PKG-INFO +3 -2
  2. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/PYPI_README.md +1 -1
  3. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/README.md +1 -1
  4. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/pyproject.toml +2 -1
  5. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/PKG-INFO +3 -2
  6. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/SOURCES.txt +2 -0
  7. pytaskwarrior-1.2.0.dev0/src/pytaskwarrior.egg-info/requires.txt +2 -0
  8. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/adapters/taskwarrior_adapter.py +57 -2
  9. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/exceptions.py +8 -0
  10. pytaskwarrior-1.2.0.dev0/src/taskwarrior/protocols/sync.py +8 -0
  11. pytaskwarrior-1.2.0.dev0/src/taskwarrior/sync_backends/sync_local.py +14 -0
  12. pytaskwarrior-1.1.0/src/pytaskwarrior.egg-info/requires.txt +0 -1
  13. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/LICENSE +0 -0
  14. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/setup.cfg +0 -0
  15. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/__init__.py +0 -0
  16. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/dependency_links.txt +0 -0
  17. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/pytaskwarrior.egg-info/top_level.txt +0 -0
  18. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/__init__.py +0 -0
  19. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/adapters/__init__.py +0 -0
  20. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/__init__.py +0 -0
  21. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/annotation_dto.py +0 -0
  22. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/context_dto.py +0 -0
  23. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/task_dto.py +0 -0
  24. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/dto/uda_dto.py +0 -0
  25. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/enums.py +0 -0
  26. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/main.py +0 -0
  27. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/py.typed +0 -0
  28. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/registry/__init__.py +0 -0
  29. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/registry/uda_registry.py +0 -0
  30. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/__init__.py +0 -0
  31. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/context_service.py +0 -0
  32. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/services/uda_service.py +0 -0
  33. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/utils/__init__.py +0 -0
  34. {pytaskwarrior-1.1.0 → pytaskwarrior-1.2.0.dev0}/src/taskwarrior/utils/conversions.py +0 -0
  35. {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.1.0
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
- **v1.0.0**: 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
+ 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
- **v1.0.0**: 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.
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.0.0**: 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.
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.1.0"
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.1.0
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
- **v1.0.0**: 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
+ 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
@@ -0,0 +1,2 @@
1
+ pydantic>=2.11.7
2
+ taskchampion-py>=2.0.2
@@ -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,8 @@
1
+ from typing import Protocol
2
+
3
+ class SyncProtocol(Protocol):
4
+ def synchronize(self) -> None:
5
+ """Perform the synchronization process."""
6
+ ...
7
+
8
+ # Future extension: add more sync-related methods as needed
@@ -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