pytaskwarrior 2.0.5__tar.gz → 2.0.7__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 (36) hide show
  1. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/PKG-INFO +1 -1
  2. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/pyproject.toml +1 -1
  3. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/PKG-INFO +1 -1
  4. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/SOURCES.txt +2 -1
  5. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/annotation_dto.py +2 -2
  6. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/task_dto.py +13 -5
  7. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/enums.py +20 -28
  8. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/main.py +8 -0
  9. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/registry/uda_registry.py +8 -0
  10. pytaskwarrior-2.0.7/tests/test_main_get_udas.py +22 -0
  11. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/LICENSE +0 -0
  12. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/PYPI_README.md +0 -0
  13. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/README.md +0 -0
  14. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/setup.cfg +0 -0
  15. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/__init__.py +0 -0
  16. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/dependency_links.txt +0 -0
  17. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/requires.txt +0 -0
  18. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/top_level.txt +0 -0
  19. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/__init__.py +0 -0
  20. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/adapters/__init__.py +0 -0
  21. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/adapters/taskwarrior_adapter.py +0 -0
  22. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/config/config_store.py +0 -0
  23. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/config/uda_parser.py +0 -0
  24. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/__init__.py +0 -0
  25. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/context_dto.py +0 -0
  26. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/task_id.py +0 -0
  27. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/uda_dto.py +0 -0
  28. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/exceptions.py +0 -0
  29. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/py.typed +0 -0
  30. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/registry/__init__.py +0 -0
  31. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/services/__init__.py +0 -0
  32. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/services/context_service.py +0 -0
  33. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/services/uda_service.py +0 -0
  34. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/utils/__init__.py +0 -0
  35. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/utils/conversions.py +0 -0
  36. {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/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.5
3
+ Version: 2.0.7
4
4
  Summary: Taskwarrior wrapper python module
5
5
  Author-email: sznicolas <sznicolas@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pytaskwarrior"
3
- version = "2.0.5"
3
+ version = "2.0.7"
4
4
  description = "Taskwarrior wrapper python module"
5
5
  readme = "PYPI_README.md"
6
6
  requires-python = ">=3.12"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytaskwarrior
3
- Version: 2.0.5
3
+ Version: 2.0.7
4
4
  Summary: Taskwarrior wrapper python module
5
5
  Author-email: sznicolas <sznicolas@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -30,4 +30,5 @@ src/taskwarrior/services/context_service.py
30
30
  src/taskwarrior/services/uda_service.py
31
31
  src/taskwarrior/utils/__init__.py
32
32
  src/taskwarrior/utils/conversions.py
33
- src/taskwarrior/utils/dto_converter.py
33
+ src/taskwarrior/utils/dto_converter.py
34
+ tests/test_main_get_udas.py
@@ -37,7 +37,7 @@ class AnnotationDTO(BaseModel):
37
37
 
38
38
  @field_validator("entry", mode="before")
39
39
  @classmethod
40
- def parse_entry_date(cls, value: str | datetime | None) -> datetime:
40
+ def parse_entry_date(cls, value: str | datetime) -> datetime:
41
41
  """Parse the entry date from TaskWarrior format.
42
42
 
43
43
  Args:
@@ -48,4 +48,4 @@ class AnnotationDTO(BaseModel):
48
48
  """
49
49
  if isinstance(value, datetime):
50
50
  return value
51
- return parse_taskwarrior_date(value or "")
51
+ return parse_taskwarrior_date(value)
@@ -82,8 +82,14 @@ class TaskInputDTO(BaseModel):
82
82
  default_factory=list, description="List of UUIDs of tasks this task depends on"
83
83
  )
84
84
  parent: UUID | None = Field(default=None, description="UUID of the template task")
85
- recur: RecurrencePeriod | None = Field(
86
- default=None, description="Recurrence period for recurring tasks"
85
+ recur: RecurrencePeriod | str | None = Field(
86
+ default=None,
87
+ description=(
88
+ "Recurrence period. "
89
+ "Use standard values: 'daily', 'weekly', 'monthly', 'yearly', 'quarterly', 'semiannually', 'hourly', 'minutely', 'secondly'. "
90
+ "OR use custom TaskWarrior expressions like '2weeks', '3days', 'every 10 days', '2months'. "
91
+ "TaskWarrior will validate the expression."
92
+ )
87
93
  )
88
94
  scheduled: str | None = Field(
89
95
  default=None,
@@ -307,7 +313,7 @@ class TaskOutputDTO(BaseModel):
307
313
  mode="before",
308
314
  )
309
315
  @classmethod
310
- def parse_datetime_field(cls, value: str | datetime | None) -> datetime:
316
+ def parse_datetime_field(cls, value: str | datetime | None) -> datetime | None:
311
317
  """Parse datetime fields from TaskWarrior format.
312
318
 
313
319
  Handles both TaskWarrior's compact format (20260101T193139Z)
@@ -317,11 +323,13 @@ class TaskOutputDTO(BaseModel):
317
323
  value: The datetime value to parse, either as string or datetime.
318
324
 
319
325
  Returns:
320
- A datetime object with timezone info.
326
+ A datetime object with timezone info, or None if value is None.
321
327
  """
328
+ if value is None:
329
+ return None
322
330
  if isinstance(value, datetime):
323
331
  return value
324
- return parse_taskwarrior_date(value or "")
332
+ return parse_taskwarrior_date(value)
325
333
 
326
334
  def get_uda(self, name: str, default: Any = None) -> Any:
327
335
  """Get a User Defined Attribute value by name.
@@ -57,34 +57,26 @@ class Priority(str, Enum):
57
57
  class RecurrencePeriod(str, Enum):
58
58
  """Supported recurrence periods for recurring tasks.
59
59
 
60
- Defines how often a recurring task should repeat. When a recurring
61
- task is completed, TaskWarrior automatically creates the next instance.
62
-
63
- Attributes:
64
- DAILY: Task repeats every day.
65
- WEEKLY: Task repeats every week.
66
- MONTHLY: Task repeats every month.
67
- YEARLY: Task repeats every year.
68
- QUARTERLY: Task repeats every quarter (3 months).
69
- SEMIANNUALLY: Task repeats every 6 months.
70
- HOURLY: Task repeats every hour.
71
- MINUTELY: Task repeats every minute.
72
- SECONDLY: Task repeats every second.
73
-
74
- Example:
75
- >>> from taskwarrior import TaskInputDTO, RecurrencePeriod
76
- >>> weekly_task = TaskInputDTO(
77
- ... description="Weekly review",
78
- ... due="monday",
79
- ... recur=RecurrencePeriod.WEEKLY
80
- ... )
81
-
82
- Note:
83
- For custom periods not listed here (e.g. ``"2weeks"``, ``"10days"``,
84
- ``"6months"``), pass a plain string directly to the ``recur`` field —
85
- TaskWarrior accepts any valid recurrence expression:
86
-
87
- >>> custom_task = TaskInputDTO(description="Bi-weekly review", recur="2weeks")
60
+ TaskWarrior supports both standard keywords and custom duration expressions.
61
+
62
+ Standard Keywords (Recommended):
63
+ - DAILY, WEEKLY, MONTHLY, YEARLY
64
+ - QUARTERLY, SEMIANNUALLY
65
+ - HOURLY, MINUTELY, SECONDLY
66
+
67
+ Custom Expressions (Also Valid):
68
+ - "2weeks" (Every two weeks)
69
+ - "3days" (Every three days)
70
+ - "every 10 days"
71
+ - "2months"
72
+ - "6months"
73
+
74
+ Example (Standard):
75
+ >>> task = TaskInputDTO(description="Daily standup", recur=RecurrencePeriod.DAILY)
76
+
77
+ Example (Custom):
78
+ >>> task = TaskInputDTO(description="Bi-weekly report", recur="2weeks")
79
+ # Note: Passing a string directly bypasses the Enum but is fully supported by TaskWarrior.
88
80
  """
89
81
 
90
82
  DAILY = "daily"
@@ -556,6 +556,14 @@ class TaskWarrior:
556
556
  """
557
557
  return self.uda_service.registry.get_uda(name)
558
558
 
559
+ def get_udas(self) -> list[UdaConfig]:
560
+ """Get full UDA definitions.
561
+
562
+ Returns:
563
+ List of UdaConfig objects for all defined UDAs.
564
+ """
565
+ return self.uda_service.registry.get_udas()
566
+
559
567
  def define_uda(self, uda: UdaConfig) -> None:
560
568
  """Define a new UDA via the TaskWarrior facade.
561
569
 
@@ -68,3 +68,11 @@ class UdaRegistry:
68
68
  def is_uda_field(self, field_name: str) -> bool:
69
69
  """Check if a field name corresponds to a registered UDA."""
70
70
  return field_name in self._udas
71
+
72
+ def get_udas(self) -> list[UdaConfig]:
73
+ """Return all registered UdaConfig objects as a list.
74
+
75
+ This provides a direct way to retrieve the full UdaConfig objects
76
+ currently stored in the registry.
77
+ """
78
+ return list(self._udas.values())
@@ -0,0 +1,22 @@
1
+
2
+ from taskwarrior import TaskWarrior
3
+
4
+
5
+ def test_get_udas_empty(monkeypatch):
6
+ tw = TaskWarrior(task_cmd="task", taskrc_file="/tmp/nonexistent", data_location=None)
7
+ monkeypatch.setattr(tw.uda_service.registry, "get_udas", lambda: [])
8
+ assert tw.get_udas() == []
9
+
10
+
11
+ def test_get_udas_multiple(monkeypatch):
12
+ tw = TaskWarrior(task_cmd="task", taskrc_file="/tmp/nonexistent", data_location=None)
13
+
14
+ class DummyUda:
15
+ def __init__(self, name):
16
+ self.name = name
17
+
18
+ names = {"sev", "est"}
19
+ monkeypatch.setattr(tw.uda_service.registry, "get_udas", lambda: [DummyUda("sev"), DummyUda("est")])
20
+
21
+ udas = tw.get_udas()
22
+ assert {u.name for u in udas} == names
File without changes
File without changes
File without changes