whatnext 3.3__tar.gz → 3.3.2__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 (126) hide show
  1. {whatnext-3.3 → whatnext-3.3.2}/CHANGELOG.md +10 -0
  2. {whatnext-3.3 → whatnext-3.3.2}/PKG-INFO +1 -1
  3. {whatnext-3.3 → whatnext-3.3.2}/docs/deferring.md +1 -1
  4. whatnext-3.3.2/tests/deferring/dir-complete/tasks.md +3 -0
  5. whatnext-3.3.2/tests/deferring/needs-dir-complete.md +3 -0
  6. whatnext-3.3.2/tests/deferring/needs-subdir.md +3 -0
  7. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring.bats +23 -2
  8. {whatnext-3.3 → whatnext-3.3.2}/tests/headerless/annotation.md +1 -0
  9. {whatnext-3.3 → whatnext-3.3.2}/tests/list.bats +1 -0
  10. {whatnext-3.3 → whatnext-3.3.2}/tests/test_file.py +55 -0
  11. {whatnext-3.3 → whatnext-3.3.2}/whatnext/models.py +3 -1
  12. {whatnext-3.3 → whatnext-3.3.2}/whatnext/whatnext.py +9 -1
  13. {whatnext-3.3 → whatnext-3.3.2}/.flake8 +0 -0
  14. {whatnext-3.3 → whatnext-3.3.2}/.github/dependabot.yml +0 -0
  15. {whatnext-3.3 → whatnext-3.3.2}/.github/workflows/publish.yml +0 -0
  16. {whatnext-3.3 → whatnext-3.3.2}/.github/workflows/tests.yml +0 -0
  17. {whatnext-3.3 → whatnext-3.3.2}/.gitignore +0 -0
  18. {whatnext-3.3 → whatnext-3.3.2}/.whatnext +0 -0
  19. {whatnext-3.3 → whatnext-3.3.2}/LICENCE +0 -0
  20. {whatnext-3.3 → whatnext-3.3.2}/LICENSE +0 -0
  21. {whatnext-3.3 → whatnext-3.3.2}/Makefile +0 -0
  22. {whatnext-3.3 → whatnext-3.3.2}/README.md +0 -0
  23. {whatnext-3.3 → whatnext-3.3.2}/archive/done/tasks.md +0 -0
  24. {whatnext-3.3 → whatnext-3.3.2}/docs/annotations.md +0 -0
  25. {whatnext-3.3 → whatnext-3.3.2}/docs/basics.md +0 -0
  26. {whatnext-3.3 → whatnext-3.3.2}/docs/deadlines.md +0 -0
  27. {whatnext-3.3 → whatnext-3.3.2}/docs/exclusions.md +0 -0
  28. {whatnext-3.3 → whatnext-3.3.2}/docs/index.md +0 -0
  29. {whatnext-3.3 → whatnext-3.3.2}/docs/installation.md +0 -0
  30. {whatnext-3.3 → whatnext-3.3.2}/docs/next.md +0 -0
  31. {whatnext-3.3 → whatnext-3.3.2}/docs/prioritisation.md +0 -0
  32. {whatnext-3.3 → whatnext-3.3.2}/docs/sequential.md +0 -0
  33. {whatnext-3.3 → whatnext-3.3.2}/docs/usage.md +0 -0
  34. {whatnext-3.3 → whatnext-3.3.2}/example/archived/projects/tangerine.md +0 -0
  35. {whatnext-3.3 → whatnext-3.3.2}/example/projects/curtain.md +0 -0
  36. {whatnext-3.3 → whatnext-3.3.2}/example/projects/fountain.md +0 -0
  37. {whatnext-3.3 → whatnext-3.3.2}/example/projects/harvest.md +0 -0
  38. {whatnext-3.3 → whatnext-3.3.2}/example/projects/obelisk.md +0 -0
  39. {whatnext-3.3 → whatnext-3.3.2}/example/projects/tinsel.md +0 -0
  40. {whatnext-3.3 → whatnext-3.3.2}/example/tasks.md +0 -0
  41. {whatnext-3.3 → whatnext-3.3.2}/example/template.md +0 -0
  42. {whatnext-3.3 → whatnext-3.3.2}/pyproject.toml +0 -0
  43. {whatnext-3.3 → whatnext-3.3.2}/scripts/update-usage-examples.py +0 -0
  44. {whatnext-3.3 → whatnext-3.3.2}/tasks/deferring.md +0 -0
  45. {whatnext-3.3 → whatnext-3.3.2}/tasks/documentation.md +0 -0
  46. {whatnext-3.3 → whatnext-3.3.2}/tasks/ignoring.md +0 -0
  47. {whatnext-3.3 → whatnext-3.3.2}/tasks/listing.md +0 -0
  48. {whatnext-3.3 → whatnext-3.3.2}/tasks/next.md +0 -0
  49. {whatnext-3.3 → whatnext-3.3.2}/tasks/summaries.md +0 -0
  50. {whatnext-3.3 → whatnext-3.3.2}/tasks/test_refactor.md +0 -0
  51. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_0.1.md +0 -0
  52. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_0.2.md +0 -0
  53. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_0.3.md +0 -0
  54. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_0.4.md +0 -0
  55. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_0.5.md +0 -0
  56. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_1.0.md +0 -0
  57. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_1.1.md +0 -0
  58. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_1.2.md +0 -0
  59. {whatnext-3.3 → whatnext-3.3.2}/tasks/version_1.3.md +0 -0
  60. {whatnext-3.3 → whatnext-3.3.2}/tasks.md +0 -0
  61. {whatnext-3.3 → whatnext-3.3.2}/tests/colour.bats +0 -0
  62. {whatnext-3.3 → whatnext-3.3.2}/tests/config/.whatnext +0 -0
  63. {whatnext-3.3 → whatnext-3.3.2}/tests/config/.whatnext.alt +0 -0
  64. {whatnext-3.3 → whatnext-3.3.2}/tests/config/apples.md +0 -0
  65. {whatnext-3.3 → whatnext-3.3.2}/tests/config/oranges.md +0 -0
  66. {whatnext-3.3 → whatnext-3.3.2}/tests/config-external +0 -0
  67. {whatnext-3.3 → whatnext-3.3.2}/tests/config.bats +0 -0
  68. {whatnext-3.3 → whatnext-3.3.2}/tests/deadline.bats +0 -0
  69. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/bare-after.md +0 -0
  70. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/circular-a.md +0 -0
  71. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/circular-b.md +0 -0
  72. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/deferred.md +0 -0
  73. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/duplicate-warnings.md +0 -0
  74. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/missing-dep.md +0 -0
  75. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/needs-absolute.md +0 -0
  76. /whatnext-3.3/tests/deferring/needs-subdir.md → /whatnext-3.3.2/tests/deferring/needs-subdir-file.md +0 -0
  77. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/phase-misplaced.md +0 -0
  78. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/phases-empty.md +0 -0
  79. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/phases-partial.md +0 -0
  80. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/phases-queue.md +0 -0
  81. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/phases.md +0 -0
  82. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/regular.md +0 -0
  83. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/root-target.md +0 -0
  84. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/samename.md +0 -0
  85. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/subdir/needs-parent.md +0 -0
  86. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/subdir/needs-sibling.md +0 -0
  87. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/subdir/samename.md +0 -0
  88. {whatnext-3.3 → whatnext-3.3.2}/tests/deferring/subdir/target.md +0 -0
  89. {whatnext-3.3 → whatnext-3.3.2}/tests/edit.bats +0 -0
  90. {whatnext-3.3 → whatnext-3.3.2}/tests/headerless/deadline.md +0 -0
  91. {whatnext-3.3 → whatnext-3.3.2}/tests/housekeeping.bats +0 -0
  92. {whatnext-3.3 → whatnext-3.3.2}/tests/invalid.md +0 -0
  93. {whatnext-3.3 → whatnext-3.3.2}/tests/matching.bats +0 -0
  94. {whatnext-3.3 → whatnext-3.3.2}/tests/next/alternate.md +0 -0
  95. {whatnext-3.3 → whatnext-3.3.2}/tests/next/append-after.md +0 -0
  96. {whatnext-3.3 → whatnext-3.3.2}/tests/next/append.md +0 -0
  97. {whatnext-3.3 → whatnext-3.3.2}/tests/next/empty.md +0 -0
  98. {whatnext-3.3 → whatnext-3.3.2}/tests/next/insert.md +0 -0
  99. {whatnext-3.3 → whatnext-3.3.2}/tests/next/projects/alpha/tasks/things.md +0 -0
  100. {whatnext-3.3 → whatnext-3.3.2}/tests/next/projects/alpha/tasks.md +0 -0
  101. {whatnext-3.3 → whatnext-3.3.2}/tests/next/projects/beta/things.md +0 -0
  102. {whatnext-3.3 → whatnext-3.3.2}/tests/next/something.txt +0 -0
  103. {whatnext-3.3 → whatnext-3.3.2}/tests/next/tasks.md +0 -0
  104. {whatnext-3.3 → whatnext-3.3.2}/tests/next/wide.md +0 -0
  105. {whatnext-3.3 → whatnext-3.3.2}/tests/next/wrapped.md +0 -0
  106. {whatnext-3.3 → whatnext-3.3.2}/tests/next.bats +0 -0
  107. {whatnext-3.3 → whatnext-3.3.2}/tests/notnext.bats +0 -0
  108. {whatnext-3.3 → whatnext-3.3.2}/tests/queue/all-complete.md +0 -0
  109. {whatnext-3.3 → whatnext-3.3.2}/tests/queue/blocked.md +0 -0
  110. {whatnext-3.3 → whatnext-3.3.2}/tests/queue/in-progress.md +0 -0
  111. {whatnext-3.3 → whatnext-3.3.2}/tests/queue/normal.md +0 -0
  112. {whatnext-3.3 → whatnext-3.3.2}/tests/queue/prioritised.md +0 -0
  113. {whatnext-3.3 → whatnext-3.3.2}/tests/queue.bats +0 -0
  114. {whatnext-3.3 → whatnext-3.3.2}/tests/summary.bats +0 -0
  115. {whatnext-3.3 → whatnext-3.3.2}/tests/test_defer.py +0 -0
  116. {whatnext-3.3 → whatnext-3.3.2}/tests/test_edit.py +0 -0
  117. {whatnext-3.3 → whatnext-3.3.2}/tests/test_limit.py +0 -0
  118. {whatnext-3.3 → whatnext-3.3.2}/tests/test_output.py +0 -0
  119. {whatnext-3.3 → whatnext-3.3.2}/tests/test_summary.py +0 -0
  120. {whatnext-3.3 → whatnext-3.3.2}/tests/test_task.py +0 -0
  121. {whatnext-3.3 → whatnext-3.3.2}/tests/usage.bats +0 -0
  122. {whatnext-3.3 → whatnext-3.3.2}/tests/version.bats +0 -0
  123. {whatnext-3.3 → whatnext-3.3.2}/whatnext/__init__.py +0 -0
  124. {whatnext-3.3 → whatnext-3.3.2}/whatnext/guide.txt +0 -0
  125. {whatnext-3.3 → whatnext-3.3.2}/whatnext/next.py +0 -0
  126. {whatnext-3.3 → whatnext-3.3.2}/whatnext/summary.py +0 -0
@@ -1,6 +1,16 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## [v3.3.2] - 2026-04-17
5
+
6
+ **Fixes:** `@after` with a directory waits on all tasks under that dir.
7
+
8
+
9
+ ## [v3.3.1] - 2026-04-17
10
+
11
+ **Fixes:** Empty tasks (`- [ ]\n`) no longer cause a stacktrace.
12
+
13
+
4
14
  ## [v3.3] - 2026-04-13
5
15
 
6
16
  **Added:**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whatnext
3
- Version: 3.3
3
+ Version: 3.3.2
4
4
  Summary: A lister of tasks found in Markdown files
5
5
  Project-URL: Homepage, https://github.com/norm/whatnext
6
6
  Project-URL: Repository, https://github.com/norm/whatnext
@@ -37,7 +37,7 @@ Filenames are resolved relative to the file containing the `@after`
37
37
  directive.
38
38
 
39
39
  @after ../project-b/setup.md
40
- @after subtask/notes.md
40
+ @after subtask/
41
41
  @after ~/tasks/dependency.md
42
42
 
43
43
  Circular dependencies (where A depends on B and B depends on A) will
@@ -0,0 +1,3 @@
1
+ # Tasks
2
+
3
+ - [X] completed task
@@ -0,0 +1,3 @@
1
+ # Waiting
2
+
3
+ - [ ] depends on directory @after dir-complete
@@ -0,0 +1,3 @@
1
+ # Waiting
2
+
3
+ - [ ] depends on directory @after subdir
@@ -154,14 +154,14 @@ teardown_file() {
154
154
 
155
155
  @test "after resolves path descending into subdirectory" {
156
156
  expected_output=$(sed -e 's/^ //' <<" EOF"
157
- tests/deferring/needs-subdir.md:
157
+ tests/deferring/needs-subdir-file.md:
158
158
  - [ ] depends on subdir file
159
159
  EOF
160
160
  )
161
161
 
162
162
  run --separate-stderr \
163
163
  whatnext \
164
- tests/deferring/needs-subdir.md \
164
+ tests/deferring/needs-subdir-file.md \
165
165
  tests/deferring/subdir/target.md
166
166
 
167
167
  diff -u <(echo "$expected_output") <(echo "$output")
@@ -392,3 +392,24 @@ teardown_file() {
392
392
  diff -u <(echo "$expected_output") <(echo "$output")
393
393
  [ $status -eq 0 ]
394
394
  }
395
+
396
+ @test "after directory with incomplete tasks hides deferred task" {
397
+ run whatnext tests/deferring/needs-subdir.md
398
+
399
+ diff -u <(echo "") <(echo "$output")
400
+ [ $status -eq 0 ]
401
+ }
402
+
403
+ @test "after directory with all complete tasks shows deferred task" {
404
+ expected_output=$(sed -e 's/^ //' <<" EOF"
405
+ tests/deferring/needs-dir-complete.md:
406
+ # Waiting
407
+ - [ ] depends on directory
408
+ EOF
409
+ )
410
+
411
+ run whatnext tests/deferring/needs-dir-complete.md
412
+
413
+ diff -u <(echo "$expected_output") <(echo "$output")
414
+ [ $status -eq 0 ]
415
+ }
@@ -3,3 +3,4 @@ This annotation should appear even without a heading.
3
3
  ```
4
4
 
5
5
  - [ ] a task without heading
6
+ - [ ]
@@ -687,6 +687,7 @@ function setup {
687
687
  ../tests/headerless/annotation.md:
688
688
  This annotation should appear even without a heading.
689
689
  - [ ] a task without heading
690
+ - [ ]
690
691
  EOF
691
692
  )
692
693
  diff -u <(echo "$expected_output") <(echo "$output")
@@ -122,6 +122,61 @@ class TestFileParsing:
122
122
  "phase": None,
123
123
  }
124
124
 
125
+ def test_empty_task(self):
126
+ file = MarkdownFile(
127
+ source_string="- [ ]",
128
+ today=date(2025, 1, 1),
129
+ )
130
+ assert len(file.tasks) == 1
131
+ assert file.tasks[0].as_dict() == {
132
+ "heading": None,
133
+ "state": State.OPEN,
134
+ "text": "",
135
+ "priority": Priority.NORMAL,
136
+ "due": None,
137
+ "imminent": None,
138
+ "annotation": None,
139
+ "line": 1,
140
+ "deferred": None,
141
+ "phase": None,
142
+ }
143
+
144
+ def test_empty_task_with_deadline(self):
145
+ # regex looks for text before @, so this becomes literal text
146
+ file = MarkdownFile(
147
+ source_string="- [ ] @2025-06-15",
148
+ today=date(2025, 1, 1),
149
+ )
150
+ assert len(file.tasks) == 1
151
+ assert file.tasks[0].text == "@2025-06-15"
152
+ assert file.tasks[0].due is None
153
+
154
+ def test_empty_task_with_after(self):
155
+ # regex looks for text before @, so this becomes literal text
156
+ file = MarkdownFile(
157
+ source_string="- [ ] @after other.md",
158
+ today=date(2025, 1, 1),
159
+ )
160
+ assert len(file.tasks) == 1
161
+ assert file.tasks[0].text == "@after other.md"
162
+ assert file.tasks[0].deferred is None
163
+
164
+ def test_text_without_space_after_marker(self):
165
+ file = MarkdownFile(
166
+ source_string="- [ ]text on the marker",
167
+ today=date(2025, 1, 1),
168
+ )
169
+ assert len(file.tasks) == 1
170
+ assert file.tasks[0].text == "text on the marker"
171
+
172
+ def test_multiple_spaces_after_marker(self):
173
+ file = MarkdownFile(
174
+ source_string="- [ ] text away from the marker",
175
+ today=date(2025, 1, 1),
176
+ )
177
+ assert len(file.tasks) == 1
178
+ assert file.tasks[0].text == "text away from the marker"
179
+
125
180
  def test_in_progress_task(self):
126
181
  file = MarkdownFile(
127
182
  source_string="- [/] in progress, this task is partially complete",
@@ -290,6 +290,8 @@ class MarkdownFile:
290
290
  @staticmethod
291
291
  def parse_deadline(text):
292
292
  match = MarkdownFile.DEADLINE_PATTERN.match(text)
293
+ if match is None:
294
+ return (None, None, text)
293
295
  cleaned = match.group(1)
294
296
  date_str = match.group(2)
295
297
  if date_str is None:
@@ -638,7 +640,7 @@ class MarkdownFile:
638
640
  self.max_task_line_width, len(continuation_line)
639
641
  )
640
642
  text += " " + continuation_line.strip()
641
- task_content = text[prefix_width:]
643
+ task_content = text[prefix_width - 1:].lstrip()
642
644
  task = self.parse_task(
643
645
  heading,
644
646
  heading_priority,
@@ -125,9 +125,17 @@ def filter_deferred(data, ignore_patterns):
125
125
  basename = os.path.basename(path)
126
126
  if is_dependency_ignored(basename, ignore_patterns):
127
127
  return True
128
+ if os.path.isdir(path):
129
+ for root, dirs, files in os.walk(path):
130
+ for filename in files:
131
+ if filename.endswith(".md"):
132
+ filepath = os.path.join(root, filename)
133
+ if not is_file_complete(filepath):
134
+ return False
135
+ return True
128
136
  if path in file_by_path:
129
137
  file = file_by_path[path]
130
- elif os.path.exists(path):
138
+ elif os.path.isfile(path):
131
139
  file = MarkdownFile(source=path, today=all_files[0].today)
132
140
  else:
133
141
  return False
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
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
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
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
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