pytest-codeblock 0.5.6__tar.gz → 0.5.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 (25) hide show
  1. {pytest_codeblock-0.5.6/src/pytest_codeblock.egg-info → pytest_codeblock-0.5.7}/PKG-INFO +7 -1
  2. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/README.rst +6 -0
  3. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/pyproject.toml +23 -2
  4. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/__init__.py +1 -1
  5. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/collector.py +7 -7
  6. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/md.py +8 -10
  7. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/rst.py +2 -0
  8. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_customisation.py +2 -2
  9. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_integration.py +66 -66
  10. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_nameless_codeblocks.py +38 -38
  11. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7/src/pytest_codeblock.egg-info}/PKG-INFO +7 -1
  12. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/LICENSE +0 -0
  13. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/setup.cfg +0 -0
  14. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/config.py +0 -0
  15. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/constants.py +0 -0
  16. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/helpers.py +0 -0
  17. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/pytestrun.py +0 -0
  18. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/__init__.py +0 -0
  19. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_pytest_codeblock.py +0 -0
  20. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_pytestrun_marker.py +0 -0
  21. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/SOURCES.txt +0 -0
  22. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/dependency_links.txt +0 -0
  23. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/entry_points.txt +0 -0
  24. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/requires.txt +0 -0
  25. {pytest_codeblock-0.5.6 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-codeblock
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: Pytest plugin to collect and test code blocks in reStructuredText and Markdown files.
5
5
  Author-email: Artur Barseghyan <artur.barseghyan@gmail.com>
6
6
  Maintainer-email: Artur Barseghyan <artur.barseghyan@gmail.com>
@@ -78,6 +78,7 @@ pytest-codeblock
78
78
  .. _openai: https://github.com/openai/openai-python
79
79
  .. _Ollama: https://github.com/ollama/ollama
80
80
  .. _tomli: https://pypi.org/project/tomli/
81
+ .. _doc8: https://doc8.readthedocs.io/
81
82
 
82
83
  .. Internal references
83
84
 
@@ -219,6 +220,11 @@ See `customisation docs`_ for more.
219
220
 
220
221
  Usage
221
222
  =====
223
+ .. note::
224
+
225
+ It's highly recommended to use `doc8`_ for catching possible markup errors,
226
+ that otherwise would be difficult to spot.
227
+
222
228
  reStructruredText usage
223
229
  -----------------------
224
230
  Any code directive, such as ``.. code-block:: python``, ``.. code:: python``,
@@ -15,6 +15,7 @@ pytest-codeblock
15
15
  .. _openai: https://github.com/openai/openai-python
16
16
  .. _Ollama: https://github.com/ollama/ollama
17
17
  .. _tomli: https://pypi.org/project/tomli/
18
+ .. _doc8: https://doc8.readthedocs.io/
18
19
 
19
20
  .. Internal references
20
21
 
@@ -156,6 +157,11 @@ See `customisation docs`_ for more.
156
157
 
157
158
  Usage
158
159
  =====
160
+ .. note::
161
+
162
+ It's highly recommended to use `doc8`_ for catching possible markup errors,
163
+ that otherwise would be difficult to spot.
164
+
159
165
  reStructruredText usage
160
166
  -----------------------
161
167
  Any code directive, such as ``.. code-block:: python``, ``.. code:: python``,
@@ -2,7 +2,7 @@
2
2
  name = "pytest-codeblock"
3
3
  description = "Pytest plugin to collect and test code blocks in reStructuredText and Markdown files."
4
4
  readme = "README.rst"
5
- version = "0.5.6"
5
+ version = "0.5.7"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
8
8
  "pytest",
@@ -264,14 +264,30 @@ ignore = [
264
264
  "CODE_OF_CONDUCT.rst",
265
265
  "LICENSE",
266
266
  "SECURITY.rst",
267
+ "docs/_implement_pytest_hooks.rst",
267
268
  "docs/changelog.rst",
268
269
  "docs/code_of_conduct.rst",
270
+ "docs/documentation.rst",
271
+ "docs/full-llms.rst",
272
+ "docs/index.rst",
273
+ "docs/llms.rst",
274
+ "docs/package.rst",
269
275
  "docs/security.rst",
270
276
  "docs/source_tree.rst",
271
277
  "docs/source_tree_full.rst",
272
278
  "docs/make.bat",
273
279
  "docs/Makefile",
274
280
  ]
281
+ order = [
282
+ "README.rst",
283
+ "CONTRIBUTING.rst",
284
+ "docs/quick_start_ref.rst",
285
+ "docs/restructured_text.rst",
286
+ "docs/markdown.rst",
287
+ "docs/cheatsheet_restructured_text.rst",
288
+ "docs/cheatsheet_markdown.rst",
289
+ "docs/customisation.rst",
290
+ ]
275
291
 
276
292
  [[tool.sphinx-source-tree.files]]
277
293
  output = "docs/source_tree_full.rst"
@@ -315,13 +331,18 @@ ignore = [
315
331
  "CODE_OF_CONDUCT.rst",
316
332
  "LICENSE",
317
333
  "SECURITY.rst",
334
+ "docs/_implement_pytest_hooks.rst",
318
335
  "docs/changelog.rst",
319
336
  "docs/code_of_conduct.rst",
337
+ "docs/documentation.rst",
338
+ "docs/full-llms.rst",
339
+ "docs/index.rst",
340
+ "docs/llms.rst",
341
+ "docs/package.rst",
320
342
  "docs/security.rst",
321
343
  "docs/source_tree.rst",
322
344
  "docs/source_tree_full.rst",
323
345
  "docs/make.bat",
324
346
  "docs/Makefile",
325
347
  "examples",
326
- "docs",
327
348
  ]
@@ -6,7 +6,7 @@ from .md import MarkdownFile
6
6
  from .rst import RSTFile
7
7
 
8
8
  __title__ = "pytest-codeblock"
9
- __version__ = "0.5.6"
9
+ __version__ = "0.5.7"
10
10
  __author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
11
11
  __copyright__ = "2025-2026 Artur Barseghyan"
12
12
  __license__ = "MIT"
@@ -28,17 +28,17 @@ def group_snippets(snippets: list[CodeSnippet]) -> list[CodeSnippet]:
28
28
  """
29
29
  Combine snippets that share a group key, using one of two modes:
30
30
 
31
- - **Merge mode** (default): snippets sharing the same name (no ``group``
31
+ - Merge mode (default): snippets sharing the same name (no ``group``
32
32
  set, or nameless/same-name continuations) are concatenated into a single
33
- test, accumulating marks and fixtures. This is the original behaviour.
34
- - **Incremental mode**: when every continuation snippet (``group`` set) in
33
+ test, accumulating marks and fixtures. This is the default behaviour.
34
+ - Incremental mode: when every continuation snippet (``group`` set) in
35
35
  a group also carries its own distinct name, emit one test per snippet.
36
36
  Each test's code is the cumulative concatenation of all preceding
37
37
  snippets plus itself, so each step is exercised in isolation.
38
38
 
39
39
  Unnamed snippets receive unique auto-keys so they are never merged.
40
40
  """
41
- # Pass 1: bucket each snippet by its group key, preserving insertion order.
41
+ # Pass 1: bucket each snippet by its group key, preserving insertion order
42
42
  buckets: dict[str, list[CodeSnippet]] = {}
43
43
  order: list[str] = []
44
44
  anon_count = 0
@@ -57,13 +57,13 @@ def group_snippets(snippets: list[CodeSnippet]) -> list[CodeSnippet]:
57
57
  order.append(key)
58
58
  buckets[key].append(sn)
59
59
 
60
- # Pass 2: emit merged or incremental snippets per bucket.
60
+ # Pass 2: emit merged or incremental snippets per bucket
61
61
  combined: list[CodeSnippet] = []
62
62
 
63
63
  for key in order:
64
64
  members = buckets[key]
65
65
  continuations = [sn for sn in members if sn.group]
66
- # Incremental only when every continuation has a distinct own name.
66
+ # Incremental only when every continuation has a distinct own name
67
67
  incremental = continuations and all(
68
68
  sn.name and sn.name != key for sn in continuations
69
69
  )
@@ -84,7 +84,7 @@ def group_snippets(snippets: list[CodeSnippet]) -> list[CodeSnippet]:
84
84
  fixtures=list(acc_fixtures),
85
85
  ))
86
86
  else:
87
- # Merge mode (original behaviour).
87
+ # Merge mode (default behaviour)
88
88
  first = members[0]
89
89
  merged_marks = list(first.marks)
90
90
  merged_fixtures = list(first.fixtures)
@@ -103,7 +103,7 @@ def parse_markdown(text: str) -> list[CodeSnippet]:
103
103
  block_indent = indent
104
104
  start_line = idx + 1
105
105
  code_buffer = []
106
- # determine name from info string or pending comment
106
+ # Determine name from info string or pending comment
107
107
  snippet_name = None
108
108
  for token in extra.split():
109
109
  if (
@@ -118,18 +118,17 @@ def parse_markdown(text: str) -> list[CodeSnippet]:
118
118
  break
119
119
  if snippet_name is None:
120
120
  snippet_name = pending_name
121
- # reset pending_name; marks stay until block closes
121
+ # Reset pending_name; marks stay until block closes
122
122
  pending_name = None
123
- continue
124
123
 
125
124
  else:
126
- # inside a fenced code block
125
+ # Inside a fenced code block
127
126
  if line.lstrip().startswith(fence):
128
- # end of block
127
+ # End of block
129
128
  in_block = False
130
129
  code_text = "\n".join(code_buffer)
131
130
  snippet_group = None
132
- # continue overrides snippet_name for grouping
131
+ # Continue overrides snippet_name for grouping
133
132
  if pending_continue:
134
133
  snippet_group = pending_continue
135
134
  pending_continue = None
@@ -141,12 +140,12 @@ def parse_markdown(text: str) -> list[CodeSnippet]:
141
140
  fixtures=pending_fixtures.copy(),
142
141
  group=snippet_group,
143
142
  ))
144
- # reset pending marks after collecting
143
+ # Reset pending marks after collecting
145
144
  pending_marks = [CODEBLOCK_MARK] # Reset to default
146
145
  snippet_name = None
147
146
  pending_fixtures.clear() # Clear pending fixtures
148
147
  else:
149
- # collect code lines (dedent by block_indent)
148
+ # Collect code lines (dedent by block_indent)
150
149
  if line.strip() == "":
151
150
  code_buffer.append("")
152
151
  else:
@@ -154,7 +153,6 @@ def parse_markdown(text: str) -> list[CodeSnippet]:
154
153
  code_buffer.append(line[block_indent:])
155
154
  else:
156
155
  code_buffer.append(line.lstrip())
157
- continue
158
156
 
159
157
  return snippets
160
158
 
@@ -278,7 +276,7 @@ class MarkdownFile(pytest.File):
278
276
  name=sn.name,
279
277
  callobj=callobj,
280
278
  )
281
- # apply any marks (e.g. django_db)
279
+ # Apply any marks (e.g. django_db)
282
280
  for m in sn.marks:
283
281
  fn.add_marker(getattr(pytest.mark, m))
284
282
  yield fn
@@ -134,6 +134,8 @@ def parse_rst(text: str, base_dir: Path) -> list[CodeSnippet]:
134
134
  fixtures=pending_fixtures.copy(),
135
135
  )
136
136
  snippets.append(snippet)
137
+ pending_marks = [CODEBLOCK_MARK]
138
+ pending_fixtures.clear()
137
139
 
138
140
  i = j + 1
139
141
  continue
@@ -32,7 +32,7 @@ x = 1
32
32
  assert snippets[0].name == "custom_lang"
33
33
  assert "x = 1" in snippets[0].code
34
34
 
35
- # -------------------------------------------------------------------------
35
+ # ------------------------------------------------------------------------
36
36
 
37
37
  def test_unknown_language_ignored(self):
38
38
  """Test that unknown language fence is ignored."""
@@ -83,7 +83,7 @@ x = 1
83
83
  snippets = parse_markdown(text)
84
84
  assert len(snippets) == 1
85
85
 
86
- # -------------------------------------------------------------------------
86
+ # ------------------------------------------------------------------------
87
87
 
88
88
  def test_default_md_extension(self):
89
89
  """Test that .md is always a supported extension."""
@@ -38,9 +38,9 @@ from ..rst import (
38
38
  )
39
39
 
40
40
 
41
- # =============================================================================
41
+ # ============================================================================
42
42
  # Test constants.py
43
- # =============================================================================
43
+ # ============================================================================
44
44
  class TestConstants:
45
45
  """Test constants module values."""
46
46
 
@@ -57,9 +57,9 @@ class TestConstants:
57
57
  assert TEST_PREFIX == "test_"
58
58
 
59
59
 
60
- # =============================================================================
60
+ # ============================================================================
61
61
  # Test collector.py - CodeSnippet dataclass
62
- # =============================================================================
62
+ # ============================================================================
63
63
  class TestCodeSnippet:
64
64
  """Test CodeSnippet dataclass."""
65
65
 
@@ -95,9 +95,9 @@ class TestCodeSnippet:
95
95
  assert "fixtures" in field_names
96
96
 
97
97
 
98
- # =============================================================================
98
+ # ============================================================================
99
99
  # Test collector.py - group_snippets function
100
- # =============================================================================
100
+ # ============================================================================
101
101
  class TestGroupSnippets:
102
102
  """Test group_snippets function."""
103
103
 
@@ -165,9 +165,9 @@ class TestGroupSnippets:
165
165
  assert "y=2" in combined[0].code
166
166
 
167
167
 
168
- # =============================================================================
168
+ # ============================================================================
169
169
  # Test helpers.py - contains_top_level_await
170
- # =============================================================================
170
+ # ============================================================================
171
171
  class TestContainsTopLevelAwait:
172
172
  """Test contains_top_level_await function."""
173
173
 
@@ -194,9 +194,9 @@ class TestContainsTopLevelAwait:
194
194
  assert contains_top_level_await("def broken(:") is False
195
195
 
196
196
 
197
- # =============================================================================
197
+ # ============================================================================
198
198
  # Test helpers.py - wrap_async_code
199
- # =============================================================================
199
+ # ============================================================================
200
200
  class TestWrapAsyncCode:
201
201
  """Test wrap_async_code function."""
202
202
 
@@ -222,9 +222,9 @@ class TestWrapAsyncCode:
222
222
  compile(wrapped, "<test>", "exec")
223
223
 
224
224
 
225
- # =============================================================================
225
+ # ============================================================================
226
226
  # Test __init__.py - pytest_collect_file hook
227
- # =============================================================================
227
+ # ============================================================================
228
228
  class TestPytestCollectFile:
229
229
  """Test pytest_collect_file hook function."""
230
230
 
@@ -292,9 +292,9 @@ class TestPytestCollectFile:
292
292
  assert isinstance(result, MarkdownFile)
293
293
 
294
294
 
295
- # =============================================================================
295
+ # ============================================================================
296
296
  # Test md.py - parse_markdown function
297
- # =============================================================================
297
+ # ============================================================================
298
298
  class TestParseMarkdown:
299
299
  """Test parse_markdown function."""
300
300
 
@@ -310,7 +310,7 @@ x = 1
310
310
  assert snippets[0].name == "test_simple"
311
311
  assert "x = 1" in snippets[0].code
312
312
 
313
- # -------------------------------------------------------------------------
313
+ # ------------------------------------------------------------------------
314
314
 
315
315
  def test_parse_with_pytestmark(self):
316
316
  """Test the <!-- pytestmark: mark --> directive."""
@@ -323,7 +323,7 @@ pass
323
323
  snippets = parse_markdown(text)
324
324
  assert "django_db" in snippets[0].marks
325
325
 
326
- # -------------------------------------------------------------------------
326
+ # ------------------------------------------------------------------------
327
327
 
328
328
  def test_parse_with_pytestfixture(self):
329
329
  """Test the <!-- pytestfixture: name --> directive."""
@@ -340,7 +340,7 @@ print("hello")
340
340
  assert "tmp_path" in snippets[0].fixtures
341
341
  assert "capsys" in snippets[0].fixtures
342
342
 
343
- # -------------------------------------------------------------------------
343
+ # ------------------------------------------------------------------------
344
344
 
345
345
  def test_parse_continue_directive(self):
346
346
  """Test the <!-- continue: name --> directive for grouping snippets."""
@@ -366,7 +366,7 @@ assert y == 2
366
366
  assert "x = 1" in test_snippets[0].code
367
367
  assert "y = x + 1" in test_snippets[0].code
368
368
 
369
- # -------------------------------------------------------------------------
369
+ # ------------------------------------------------------------------------
370
370
 
371
371
  def test_parse_incremental_continuation(self):
372
372
  """Named continuation blocks produce N cumulative tests."""
@@ -398,7 +398,7 @@ something = Exception("")
398
398
  assert 'something = "a"' in grouped[2].code
399
399
  assert 'something = Exception("")' in grouped[2].code
400
400
 
401
- # -------------------------------------------------------------------------
401
+ # ------------------------------------------------------------------------
402
402
 
403
403
  def test_parse_codeblock_name_directive(self):
404
404
  """Test the <!-- codeblock-name: name --> directive."""
@@ -414,7 +414,7 @@ assert z == 42
414
414
  assert len(snippets) == 1
415
415
  assert snippets[0].name == "test_named"
416
416
 
417
- # -------------------------------------------------------------------------
417
+ # ------------------------------------------------------------------------
418
418
 
419
419
  def test_parse_py_language(self):
420
420
  """Test markdown with 'py' as language identifier."""
@@ -427,7 +427,7 @@ x = 1
427
427
  assert len(snippets) == 1
428
428
  assert snippets[0].name == "test_py_lang"
429
429
 
430
- # -------------------------------------------------------------------------
430
+ # ------------------------------------------------------------------------
431
431
 
432
432
  def test_parse_python3_language(self):
433
433
  """Test markdown with 'python3' as language identifier."""
@@ -440,7 +440,7 @@ x = 1
440
440
  assert len(snippets) == 1
441
441
  assert snippets[0].name == "test_python3"
442
442
 
443
- # -------------------------------------------------------------------------
443
+ # ------------------------------------------------------------------------
444
444
 
445
445
  def test_parse_non_python_codeblock_ignored(self):
446
446
  """Test that non-Python code blocks are skipped."""
@@ -458,7 +458,7 @@ x = 1
458
458
  assert len(snippets) == 1
459
459
  assert snippets[0].name == "test_py"
460
460
 
461
- # -------------------------------------------------------------------------
461
+ # ------------------------------------------------------------------------
462
462
 
463
463
  def test_parse_name_colon_syntax(self):
464
464
  """Test name= vs name: syntax in fence info string."""
@@ -470,7 +470,7 @@ x = 1
470
470
  snippets = parse_markdown(text)
471
471
  assert snippets[0].name == "test_colon"
472
472
 
473
- # -------------------------------------------------------------------------
473
+ # ------------------------------------------------------------------------
474
474
 
475
475
  def test_parse_empty_codeblock(self):
476
476
  """Test parse empty code block."""
@@ -482,7 +482,7 @@ x = 1
482
482
  assert len(snippets) == 1
483
483
  assert snippets[0].code == ""
484
484
 
485
- # -------------------------------------------------------------------------
485
+ # ------------------------------------------------------------------------
486
486
 
487
487
  def test_parse_indented_fence(self):
488
488
  """Test fence with indentation."""
@@ -494,7 +494,7 @@ x = 1
494
494
  snippets = parse_markdown(text)
495
495
  assert len(snippets) == 1
496
496
 
497
- # -------------------------------------------------------------------------
497
+ # ------------------------------------------------------------------------
498
498
 
499
499
  # TODO: Remove?
500
500
  def test_parse_fence_regex_edge_case(self):
@@ -508,7 +508,7 @@ x = 1
508
508
  snippets = parse_markdown(text)
509
509
  assert len(snippets) == 1
510
510
 
511
- # -------------------------------------------------------------------------
511
+ # ------------------------------------------------------------------------
512
512
 
513
513
  def test_parse_markdown_mixed_indentation(self):
514
514
  """Test parsing codeblock with mixed indentation levels."""
@@ -524,7 +524,7 @@ x = 1
524
524
  # Code should be dedented based on fence indentation
525
525
  assert "x = 1" in snippets[0].code
526
526
 
527
- # -------------------------------------------------------------------------
527
+ # ------------------------------------------------------------------------
528
528
 
529
529
  def test_parse_short_line_in_block(self):
530
530
  """Test code block with line shorter than indent."""
@@ -541,9 +541,9 @@ y
541
541
  # The short line 'y' should still be captured
542
542
  assert "y" in snippets[0].code or "x = 1" in snippets[0].code
543
543
 
544
- # =============================================================================
544
+ # ============================================================================
545
545
  # Test rst.py - resolve_literalinclude_path
546
- # =============================================================================
546
+ # ============================================================================
547
547
  class TestResolveLiteralincludePath:
548
548
  """Test resolve_literalinclude_path function."""
549
549
 
@@ -584,9 +584,9 @@ class TestResolveLiteralincludePath:
584
584
  assert result is None
585
585
 
586
586
 
587
- # =============================================================================
587
+ # ============================================================================
588
588
  # Test rst.py - get_literalinclude_content
589
- # =============================================================================
589
+ # ============================================================================
590
590
  class TestGetLiteralincludeContent:
591
591
  """Test get_literalinclude_content function."""
592
592
 
@@ -605,9 +605,9 @@ class TestGetLiteralincludeContent:
605
605
  get_literalinclude_content(str(tmp_path / "missing.py"))
606
606
 
607
607
 
608
- # =============================================================================
608
+ # ============================================================================
609
609
  # Test rst.py - parse_rst function
610
- # =============================================================================
610
+ # ============================================================================
611
611
  class TestParseRst:
612
612
  """Test parse_rst function."""
613
613
 
@@ -623,7 +623,7 @@ class TestParseRst:
623
623
  assert len(snippets) == 1
624
624
  assert snippets[0].name == "test_rst"
625
625
 
626
- # -------------------------------------------------------------------------
626
+ # ------------------------------------------------------------------------
627
627
 
628
628
  def test_parse_code_directive(self, tmp_path):
629
629
  """Test .. code:: python (alternative to code-block)."""
@@ -637,7 +637,7 @@ class TestParseRst:
637
637
  assert len(snippets) == 1
638
638
  assert snippets[0].name == "test_code"
639
639
 
640
- # -------------------------------------------------------------------------
640
+ # ------------------------------------------------------------------------
641
641
 
642
642
  def test_parse_pytestmark(self, tmp_path):
643
643
  rst = """
@@ -651,7 +651,7 @@ class TestParseRst:
651
651
  snippets = parse_rst(rst, tmp_path)
652
652
  assert "django_db" in snippets[0].marks
653
653
 
654
- # -------------------------------------------------------------------------
654
+ # ------------------------------------------------------------------------
655
655
 
656
656
  def test_parse_pytestfixture(self, tmp_path):
657
657
  """Test the .. pytestfixture: directive."""
@@ -668,7 +668,7 @@ class TestParseRst:
668
668
  assert len(snippets) == 1
669
669
  assert "tmp_path" in snippets[0].fixtures
670
670
 
671
- # -------------------------------------------------------------------------
671
+ # ------------------------------------------------------------------------
672
672
 
673
673
  def test_parse_continue_directive(self, tmp_path):
674
674
  """Test the .. continue: directive for grouping RST snippets."""
@@ -695,7 +695,7 @@ Some text.
695
695
  assert "a = 10" in test_snippets[0].code
696
696
  assert "b = a + 5" in test_snippets[0].code
697
697
 
698
- # -------------------------------------------------------------------------
698
+ # ------------------------------------------------------------------------
699
699
 
700
700
  def test_parse_codeblock_name(self, tmp_path):
701
701
  rst = """
@@ -708,7 +708,7 @@ Some text.
708
708
  snippets = parse_rst(rst, tmp_path)
709
709
  assert snippets[0].name == "test_named"
710
710
 
711
- # -------------------------------------------------------------------------
711
+ # ------------------------------------------------------------------------
712
712
 
713
713
  def test_parse_literal_block(self, tmp_path):
714
714
  """Test parsing of literal blocks via :: syntax."""
@@ -726,7 +726,7 @@ assert result == 3
726
726
  assert snippets[0].name == "test_literal"
727
727
  assert "result = 1 + 2" in snippets[0].code
728
728
 
729
- # -------------------------------------------------------------------------
729
+ # ------------------------------------------------------------------------
730
730
 
731
731
  def test_parse_rst_continue_in_literal_block(self, tmp_path):
732
732
  """Test continue directive with literal block syntax."""
@@ -751,7 +751,7 @@ b = 2
751
751
  matching = [s for s in grouped if s.name == "test_lit_continue"]
752
752
  assert len(matching) >= 1
753
753
 
754
- # -------------------------------------------------------------------------
754
+ # ------------------------------------------------------------------------
755
755
 
756
756
  def test_parse_literalinclude(self, tmp_path):
757
757
  """Test literalinclude directive with test_ name."""
@@ -766,7 +766,7 @@ b = 2
766
766
  assert len(snippets) == 1
767
767
  assert "def hello():" in snippets[0].code
768
768
 
769
- # -------------------------------------------------------------------------
769
+ # ------------------------------------------------------------------------
770
770
 
771
771
  def test_parse_literalinclude_no_test_prefix(self, tmp_path):
772
772
  """Test literalinclude without test_ prefix is skipped."""
@@ -780,7 +780,7 @@ b = 2
780
780
  # Should be empty because name doesn't start with test_
781
781
  assert len(snippets) == 0
782
782
 
783
- # -------------------------------------------------------------------------
783
+ # ------------------------------------------------------------------------
784
784
 
785
785
  def test_parse_non_python_code_block(self, tmp_path):
786
786
  """Non-python code blocks are skipped."""
@@ -792,7 +792,7 @@ b = 2
792
792
  snippets = parse_rst(rst, tmp_path)
793
793
  assert len(snippets) == 0
794
794
 
795
- # -------------------------------------------------------------------------
795
+ # ------------------------------------------------------------------------
796
796
 
797
797
  def test_parse_wrong_indent(self, tmp_path):
798
798
  """Code at wrong indent level."""
@@ -807,7 +807,7 @@ x = 1
807
807
  # Should not collect this as a valid snippet
808
808
  assert len(snippets) == 0
809
809
 
810
- # -------------------------------------------------------------------------
810
+ # ------------------------------------------------------------------------
811
811
 
812
812
  def test_parse_literal_codeblock_eof(self, tmp_path):
813
813
  """Test literal block at end of file."""
@@ -820,7 +820,7 @@ Block::"""
820
820
  # Should handle gracefully
821
821
  assert len(snippets) == 0
822
822
 
823
- # -------------------------------------------------------------------------
823
+ # ------------------------------------------------------------------------
824
824
 
825
825
  def test_parse_empty_codeblock(self, tmp_path):
826
826
  """Test parsing an empty code block."""
@@ -833,7 +833,7 @@ Block::"""
833
833
  # Empty blocks are collected but have no snippets
834
834
  assert len(snippets) == 0
835
835
 
836
- # -------------------------------------------------------------------------
836
+ # ------------------------------------------------------------------------
837
837
 
838
838
  def test_parse_literal_block_empty_line_after(self, tmp_path):
839
839
  """Test literal block with just empty line after (edge case)."""
@@ -848,13 +848,13 @@ Block::
848
848
  assert len(snippets) == 0
849
849
 
850
850
 
851
- # =============================================================================
851
+ # ============================================================================
852
852
  # Integration tests using pytester - exercises collectors and hook
853
- # =============================================================================
853
+ # ============================================================================
854
854
 
855
- # -----------------------------------------------------------------------------
855
+ # ----------------------------------------------------------------------------
856
856
  # Test RSTFile.collect() method
857
- # -----------------------------------------------------------------------------
857
+ # ----------------------------------------------------------------------------
858
858
 
859
859
  class TestMarkdownCollector:
860
860
  """Integration tests for MarkdownFile collector."""
@@ -876,7 +876,7 @@ assert x == 1
876
876
  result.assert_outcomes(passed=1)
877
877
  assert "test_basic" in result.stdout.str()
878
878
 
879
- # -------------------------------------------------------------------------
879
+ # ------------------------------------------------------------------------
880
880
 
881
881
  def test_collect_with_fixture(self, pytester_subprocess):
882
882
  """Test that fixtures are properly injected."""
@@ -892,7 +892,7 @@ assert tmp_path.exists()
892
892
  result = pytester_subprocess.runpytest("-v", "-p", "no:django")
893
893
  result.assert_outcomes(passed=1)
894
894
 
895
- # -------------------------------------------------------------------------
895
+ # ------------------------------------------------------------------------
896
896
 
897
897
  def test_collect_async_code(self, pytester_subprocess):
898
898
  """Test that async code is automatically wrapped."""
@@ -908,7 +908,7 @@ await asyncio.sleep(0)
908
908
  result = pytester_subprocess.runpytest("-v", "-p", "no:django")
909
909
  result.assert_outcomes(passed=1)
910
910
 
911
- # -------------------------------------------------------------------------
911
+ # ------------------------------------------------------------------------
912
912
 
913
913
  def test_syntax_error_reporting(self, pytester_subprocess):
914
914
  """Test that syntax errors in snippets are properly reported."""
@@ -928,7 +928,7 @@ def broken(:
928
928
  or "syntax" in result.stdout.str().lower()
929
929
  )
930
930
 
931
- # -------------------------------------------------------------------------
931
+ # ------------------------------------------------------------------------
932
932
 
933
933
  def test_runtime_error_reporting(self, pytester_subprocess):
934
934
  """Test that runtime errors in snippets are properly reported."""
@@ -944,7 +944,7 @@ raise ValueError("intentional error")
944
944
  result.assert_outcomes(failed=1)
945
945
  assert "ValueError" in result.stdout.str()
946
946
 
947
- # -------------------------------------------------------------------------
947
+ # ------------------------------------------------------------------------
948
948
 
949
949
  def test_incremental_continue_collects_separate_tests(
950
950
  self, pytester_subprocess
@@ -979,9 +979,9 @@ assert isinstance(something, Exception)
979
979
  assert "test_step_three" in stdout
980
980
 
981
981
 
982
- # -----------------------------------------------------------------------------
982
+ # ----------------------------------------------------------------------------
983
983
  # Test RSTFile.collect() method
984
- # -----------------------------------------------------------------------------
984
+ # ----------------------------------------------------------------------------
985
985
 
986
986
  class TestRSTCollector:
987
987
  """Integration tests for RSTFile collector."""
@@ -1005,7 +1005,7 @@ Test File
1005
1005
  result.assert_outcomes(passed=1)
1006
1006
  assert "test_rst_basic" in result.stdout.str()
1007
1007
 
1008
- # -------------------------------------------------------------------------
1008
+ # ------------------------------------------------------------------------
1009
1009
 
1010
1010
  def test_collect_with_fixture(self, pytester_subprocess):
1011
1011
  """Test that RST fixtures are properly injected."""
@@ -1023,7 +1023,7 @@ Test File
1023
1023
  result = pytester_subprocess.runpytest("-v", "-p", "no:django")
1024
1024
  result.assert_outcomes(passed=1)
1025
1025
 
1026
- # -------------------------------------------------------------------------
1026
+ # ------------------------------------------------------------------------
1027
1027
 
1028
1028
  def test_collect_async_code(self, pytester_subprocess):
1029
1029
  """Test that RST async code is automatically wrapped."""
@@ -1040,7 +1040,7 @@ Test File
1040
1040
  result = pytester_subprocess.runpytest("-v", "-p", "no:django")
1041
1041
  result.assert_outcomes(passed=1)
1042
1042
 
1043
- # -------------------------------------------------------------------------
1043
+ # ------------------------------------------------------------------------
1044
1044
 
1045
1045
  def test_syntax_error_reporting(self, pytester_subprocess):
1046
1046
  """Test that syntax errors in RST snippets are reported."""
@@ -1058,9 +1058,9 @@ Test File
1058
1058
  result.assert_outcomes(failed=1)
1059
1059
 
1060
1060
 
1061
- # ---------------------------------------------------------------------------
1061
+ # ----------------------------------------------------------------------------
1062
1062
  # Tests for pytest_collect_file hook dispatch
1063
- # ---------------------------------------------------------------------------
1063
+ # ----------------------------------------------------------------------------
1064
1064
 
1065
1065
 
1066
1066
  class TestPytestCollectFileHook:
@@ -1081,7 +1081,7 @@ assert True
1081
1081
  )
1082
1082
  assert "test_md_hook" in result.stdout.str()
1083
1083
 
1084
- # -------------------------------------------------------------------------
1084
+ # ------------------------------------------------------------------------
1085
1085
 
1086
1086
  def test_hook_dispatches_rst(self, pytester_subprocess):
1087
1087
  """Test that .rst files are dispatched to RSTFile."""
@@ -1099,7 +1099,7 @@ assert True
1099
1099
  )
1100
1100
  assert "test_rst_hook" in result.stdout.str()
1101
1101
 
1102
- # -------------------------------------------------------------------------
1102
+ # ------------------------------------------------------------------------
1103
1103
 
1104
1104
  def test_hook_ignores_other_files(self, pytester_subprocess):
1105
1105
  """Test that non-.md/.rst files are ignored."""
@@ -33,9 +33,9 @@ __all__ = (
33
33
  )
34
34
 
35
35
 
36
- # =============================================================================
36
+ # ============================================================================
37
37
  # Test Configuration Loading
38
- # =============================================================================
38
+ # ============================================================================
39
39
  class TestConfigLoading:
40
40
  """Test that test_nameless_codeblocks config loads correctly."""
41
41
 
@@ -72,9 +72,9 @@ class TestConfigLoading:
72
72
  assert config.test_nameless_codeblocks is False
73
73
 
74
74
 
75
- # =============================================================================
75
+ # ============================================================================
76
76
  # Test Auto-naming Logic
77
- # =============================================================================
77
+ # ============================================================================
78
78
  class TestAutoNaming:
79
79
  """Test the auto-naming scheme for nameless code blocks."""
80
80
 
@@ -107,9 +107,9 @@ class TestAutoNaming:
107
107
  assert module_name == "my.doc"
108
108
 
109
109
 
110
- # =============================================================================
110
+ # ============================================================================
111
111
  # Test Markdown Collector with Nameless Blocks
112
- # =============================================================================
112
+ # ============================================================================
113
113
  class TestMarkdownNameless:
114
114
  """Test MarkdownFile collector with test_nameless_codeblocks."""
115
115
 
@@ -132,7 +132,7 @@ y = 2
132
132
  assert "x = 1" in snippets[0].code
133
133
  assert "y = 2" in snippets[1].code
134
134
 
135
- # -------------------------------------------------------------------------
135
+ # ------------------------------------------------------------------------
136
136
 
137
137
  def test_parse_markdown_mixed_named_and_nameless(self):
138
138
  """Test parsing mix of named and nameless blocks."""
@@ -160,7 +160,7 @@ d = 4
160
160
  assert snippets[2].name == "test_two"
161
161
  assert snippets[3].name is None
162
162
 
163
- # -------------------------------------------------------------------------
163
+ # ------------------------------------------------------------------------
164
164
 
165
165
  def test_collect_nameless_disabled_default(self):
166
166
  """Test that nameless blocks are ignored by default."""
@@ -208,7 +208,7 @@ y = 2
208
208
  assert len(tests) == 1
209
209
  assert tests[0].name == "test_explicit"
210
210
 
211
- # -------------------------------------------------------------------------
211
+ # ------------------------------------------------------------------------
212
212
 
213
213
  def test_collect_nameless_enabled(self):
214
214
  """Test that nameless blocks are collected when enabled."""
@@ -251,7 +251,7 @@ z = 3
251
251
  assert tests[1].name == "test_test_1"
252
252
  assert tests[2].name == "test_test_2"
253
253
 
254
- # -------------------------------------------------------------------------
254
+ # ------------------------------------------------------------------------
255
255
 
256
256
  def test_auto_naming_preserves_code(self):
257
257
  """Test that auto-naming doesn't modify the code content."""
@@ -281,7 +281,7 @@ assert original_code == "unchanged"
281
281
  assert "original_code" in tests[0].code
282
282
  assert "unchanged" in tests[0].code
283
283
 
284
- # -------------------------------------------------------------------------
284
+ # ------------------------------------------------------------------------
285
285
 
286
286
  def test_auto_naming_preserves_marks(self):
287
287
  """Test that auto-naming preserves pytest marks."""
@@ -310,7 +310,7 @@ user = User.objects.first()
310
310
  assert len(tests) == 1
311
311
  assert "django_db" in tests[0].marks
312
312
 
313
- # -------------------------------------------------------------------------
313
+ # ------------------------------------------------------------------------
314
314
 
315
315
  def test_codeblock_marks_on_all_blocks(self):
316
316
  """Test that all blocks have default codeblock marks."""
@@ -331,7 +331,7 @@ assert True
331
331
  for sn in raw:
332
332
  assert CODEBLOCK_MARK in sn.marks
333
333
 
334
- # -------------------------------------------------------------------------
334
+ # ------------------------------------------------------------------------
335
335
 
336
336
  def test_auto_naming_preserves_fixtures(self):
337
337
  """Test that auto-naming preserves pytest fixtures."""
@@ -363,9 +363,9 @@ d.mkdir()
363
363
  assert "capsys" in tests[0].fixtures
364
364
 
365
365
 
366
- # =============================================================================
366
+ # ============================================================================
367
367
  # Test RST Collector with Nameless Blocks
368
- # =============================================================================
368
+ # ============================================================================
369
369
  class TestRSTNameless:
370
370
  """Test RSTFile collector with test_nameless_codeblocks."""
371
371
 
@@ -388,7 +388,7 @@ class TestRSTNameless:
388
388
  assert "x = 1" in snippets[0].code
389
389
  assert "y = 2" in snippets[1].code
390
390
 
391
- # -------------------------------------------------------------------------
391
+ # ------------------------------------------------------------------------
392
392
 
393
393
  def test_parse_rst_mixed_named_and_nameless(self, tmp_path):
394
394
  """Test parsing mix of named and nameless blocks."""
@@ -418,7 +418,7 @@ class TestRSTNameless:
418
418
  assert snippets[2].name == "test_two"
419
419
  assert snippets[3].name is None
420
420
 
421
- # -------------------------------------------------------------------------
421
+ # ------------------------------------------------------------------------
422
422
 
423
423
  def test_collect_nameless_disabled_default(self, tmp_path):
424
424
  """Test that nameless blocks are ignored by default in RST."""
@@ -462,7 +462,7 @@ class TestRSTNameless:
462
462
  assert len(tests) == 1
463
463
  assert tests[0].name == "test_explicit"
464
464
 
465
- # -------------------------------------------------------------------------
465
+ # ------------------------------------------------------------------------
466
466
 
467
467
  def test_collect_nameless_enabled(self, tmp_path):
468
468
  """Test that nameless blocks are collected when enabled in RST."""
@@ -507,7 +507,7 @@ class TestRSTNameless:
507
507
  assert tests[1].name == "test_test_1"
508
508
  assert tests[2].name == "test_test_2"
509
509
 
510
- # -------------------------------------------------------------------------
510
+ # ------------------------------------------------------------------------
511
511
 
512
512
  def test_auto_naming_preserves_code_rst(self, tmp_path):
513
513
  """Test that auto-naming doesn't modify the code content in RST."""
@@ -539,7 +539,7 @@ class TestRSTNameless:
539
539
  assert "original_code" in tests[0].code
540
540
  assert "unchanged" in tests[0].code
541
541
 
542
- # -------------------------------------------------------------------------
542
+ # ------------------------------------------------------------------------
543
543
 
544
544
  def test_auto_naming_preserves_marks_rst(self, tmp_path):
545
545
  """Test that auto-naming preserves pytest marks in RST."""
@@ -571,7 +571,7 @@ class TestRSTNameless:
571
571
  assert len(tests) == 1
572
572
  assert "django_db" in tests[0].marks
573
573
 
574
- # -------------------------------------------------------------------------
574
+ # ------------------------------------------------------------------------
575
575
 
576
576
  def test_codeblock_marks_on_all_blocks_rst(self, tmp_path):
577
577
  """Test that all blocks in RST file have default codeblock mark."""
@@ -594,7 +594,7 @@ class TestRSTNameless:
594
594
  for sn in raw:
595
595
  assert CODEBLOCK_MARK in sn.marks
596
596
 
597
- # -------------------------------------------------------------------------
597
+ # ------------------------------------------------------------------------
598
598
 
599
599
  def test_auto_naming_preserves_fixtures_rst(self, tmp_path):
600
600
  """Test that auto-naming preserves pytest fixtures in RST."""
@@ -629,9 +629,9 @@ class TestRSTNameless:
629
629
  assert "capsys" in tests[0].fixtures
630
630
 
631
631
 
632
- # =============================================================================
632
+ # ============================================================================
633
633
  # Test Integration Scenarios
634
- # =============================================================================
634
+ # ============================================================================
635
635
  @pytest.mark.skip(
636
636
  reason="Skip due to pytest 9 py.path.local deprecation issue in hooks"
637
637
  )
@@ -670,7 +670,7 @@ assert z == 3
670
670
  assert "test_integration_1" in result.stdout.str()
671
671
  assert "test_integration_2" in result.stdout.str()
672
672
 
673
- # -------------------------------------------------------------------------
673
+ # ------------------------------------------------------------------------
674
674
 
675
675
  def test_rst_nameless_integration(self, pytester):
676
676
  """Test nameless blocks work end-to-end in RST."""
@@ -706,7 +706,7 @@ Test File
706
706
  assert "test_integration_1" in result.stdout.str()
707
707
  assert "test_integration_2" in result.stdout.str()
708
708
 
709
- # -------------------------------------------------------------------------
709
+ # ------------------------------------------------------------------------
710
710
 
711
711
  def test_nameless_disabled_integration(self, pytester):
712
712
  """Test that nameless blocks are ignored when disabled."""
@@ -730,7 +730,7 @@ assert y == 2
730
730
  assert "test_explicit" in result.stdout.str()
731
731
  assert "test_default_1" not in result.stdout.str()
732
732
 
733
- # -------------------------------------------------------------------------
733
+ # ------------------------------------------------------------------------
734
734
 
735
735
  def test_multiple_files_separate_counters(self, pytester):
736
736
  """Test that each file has its own counter."""
@@ -768,9 +768,9 @@ b = 2
768
768
  assert "test_file2_2" in result.stdout.str()
769
769
 
770
770
 
771
- # =============================================================================
771
+ # ============================================================================
772
772
  # Test Edge Cases
773
- # =============================================================================
773
+ # ============================================================================
774
774
  class TestEdgeCases:
775
775
  """Test edge cases and corner scenarios."""
776
776
 
@@ -801,7 +801,7 @@ z = 3
801
801
  result = pytester.runpytest("-v", "-p", "no:django")
802
802
  result.assert_outcomes(passed=3)
803
803
 
804
- # -------------------------------------------------------------------------
804
+ # ------------------------------------------------------------------------
805
805
 
806
806
  @pytest.mark.skip(
807
807
  reason="Skip due to pytest 9 py.path.local deprecation issue in hooks"
@@ -830,7 +830,7 @@ y = 2
830
830
  # No auto-generated names
831
831
  assert "test_only_named_1" not in result.stdout.str()
832
832
 
833
- # -------------------------------------------------------------------------
833
+ # ------------------------------------------------------------------------
834
834
 
835
835
  @pytest.mark.skip(
836
836
  reason="Skip due to pytest 9 py.path.local deprecation issue in hooks"
@@ -857,7 +857,7 @@ assert x == 1
857
857
  # Should have at least the non-empty one
858
858
  assert result.ret == 0
859
859
 
860
- # -------------------------------------------------------------------------
860
+ # ------------------------------------------------------------------------
861
861
 
862
862
  @pytest.mark.skip(
863
863
  reason="Skip due to pytest 9 py.path.local deprecation issue in hooks"
@@ -887,7 +887,7 @@ y = 2
887
887
  result.assert_outcomes(passed=2)
888
888
  # Only Python blocks should be collected
889
889
 
890
- # -------------------------------------------------------------------------
890
+ # ------------------------------------------------------------------------
891
891
 
892
892
  def test_nameless_with_continue_directive_md(self):
893
893
  """Test nameless blocks with continue directive in Markdown."""
@@ -916,7 +916,7 @@ assert y == 2
916
916
  assert "x = 1" in combined[0].code
917
917
  assert "y = x + 1" in combined[0].code
918
918
 
919
- # -------------------------------------------------------------------------
919
+ # ------------------------------------------------------------------------
920
920
 
921
921
  def test_nameless_with_continue_directive_rst(self, tmp_path):
922
922
  """Test nameless blocks with continue directive in RST."""
@@ -949,7 +949,7 @@ assert y == 2
949
949
  assert "x = 1" in combined[0].code
950
950
  assert "y = x + 1" in combined[0].code
951
951
 
952
- # -------------------------------------------------------------------------
952
+ # ------------------------------------------------------------------------
953
953
 
954
954
  def test_counter_only_increments_for_nameless(self):
955
955
  """Test that counter only increments for nameless blocks."""
@@ -1008,7 +1008,7 @@ f = 6
1008
1008
  assert tests[3].name == "test_test_2"
1009
1009
  assert tests[5].name == "test_test_3"
1010
1010
 
1011
- # -------------------------------------------------------------------------
1011
+ # ------------------------------------------------------------------------
1012
1012
 
1013
1013
  def test_filename_with_special_characters(self):
1014
1014
  """Test auto-naming with special characters in filename."""
@@ -1018,7 +1018,7 @@ f = 6
1018
1018
  auto_name = f"test_{module_name}_1"
1019
1019
  assert auto_name == "test_my-test_file_1"
1020
1020
 
1021
- # -------------------------------------------------------------------------
1021
+ # ------------------------------------------------------------------------
1022
1022
 
1023
1023
  @pytest.mark.skip(
1024
1024
  reason="Skip due to pytest 9 py.path.local deprecation issue in hooks"
@@ -1041,7 +1041,7 @@ x = 1
1041
1041
  # No tests should be collected
1042
1042
  assert result.ret == 5 # Exit code 5 = no tests collected
1043
1043
 
1044
- # -------------------------------------------------------------------------
1044
+ # ------------------------------------------------------------------------
1045
1045
 
1046
1046
  @pytest.mark.skip(
1047
1047
  reason="Skip due to pytest 9 py.path.local deprecation issue in hooks"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-codeblock
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: Pytest plugin to collect and test code blocks in reStructuredText and Markdown files.
5
5
  Author-email: Artur Barseghyan <artur.barseghyan@gmail.com>
6
6
  Maintainer-email: Artur Barseghyan <artur.barseghyan@gmail.com>
@@ -78,6 +78,7 @@ pytest-codeblock
78
78
  .. _openai: https://github.com/openai/openai-python
79
79
  .. _Ollama: https://github.com/ollama/ollama
80
80
  .. _tomli: https://pypi.org/project/tomli/
81
+ .. _doc8: https://doc8.readthedocs.io/
81
82
 
82
83
  .. Internal references
83
84
 
@@ -219,6 +220,11 @@ See `customisation docs`_ for more.
219
220
 
220
221
  Usage
221
222
  =====
223
+ .. note::
224
+
225
+ It's highly recommended to use `doc8`_ for catching possible markup errors,
226
+ that otherwise would be difficult to spot.
227
+
222
228
  reStructruredText usage
223
229
  -----------------------
224
230
  Any code directive, such as ``.. code-block:: python``, ``.. code:: python``,