pytest-codeblock 0.5.5__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.
- {pytest_codeblock-0.5.5/src/pytest_codeblock.egg-info → pytest_codeblock-0.5.7}/PKG-INFO +9 -3
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/README.rst +8 -2
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/pyproject.toml +23 -2
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/__init__.py +1 -1
- pytest_codeblock-0.5.7/src/pytest_codeblock/collector.py +104 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/md.py +12 -14
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/rst.py +10 -6
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_customisation.py +2 -2
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_integration.py +139 -55
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_nameless_codeblocks.py +38 -38
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_pytest_codeblock.py +15 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_pytestrun_marker.py +5 -5
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7/src/pytest_codeblock.egg-info}/PKG-INFO +9 -3
- pytest_codeblock-0.5.5/src/pytest_codeblock/collector.py +0 -52
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/LICENSE +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/setup.cfg +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/config.py +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/constants.py +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/helpers.py +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/pytestrun.py +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/__init__.py +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/SOURCES.txt +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/dependency_links.txt +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/entry_points.txt +0 -0
- {pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock.egg-info/requires.txt +0 -0
- {pytest_codeblock-0.5.5 → 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.
|
|
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
|
|
|
@@ -146,8 +147,8 @@ Prerequisites
|
|
|
146
147
|
Documentation
|
|
147
148
|
=============
|
|
148
149
|
- Documentation is available on `Read the Docs`_.
|
|
149
|
-
- For `reStructuredText
|
|
150
|
-
- For `Markdown
|
|
150
|
+
- For `reStructuredText`, see a dedicated `reStructuredText docs`_.
|
|
151
|
+
- For `Markdown`, see a dedicated `Markdown docs`_.
|
|
151
152
|
- Both `reStructuredText docs`_ and `Markdown docs`_ have extensive
|
|
152
153
|
documentation on `pytest`_ markers and corresponding ``conftest.py`` hooks.
|
|
153
154
|
- For guidelines on contributing check the `Contributor guidelines`_.
|
|
@@ -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
|
|
|
@@ -83,8 +84,8 @@ Prerequisites
|
|
|
83
84
|
Documentation
|
|
84
85
|
=============
|
|
85
86
|
- Documentation is available on `Read the Docs`_.
|
|
86
|
-
- For `reStructuredText
|
|
87
|
-
- For `Markdown
|
|
87
|
+
- For `reStructuredText`, see a dedicated `reStructuredText docs`_.
|
|
88
|
+
- For `Markdown`, see a dedicated `Markdown docs`_.
|
|
88
89
|
- Both `reStructuredText docs`_ and `Markdown docs`_ have extensive
|
|
89
90
|
documentation on `pytest`_ markers and corresponding ``conftest.py`` hooks.
|
|
90
91
|
- For guidelines on contributing check the `Contributor guidelines`_.
|
|
@@ -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.
|
|
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.
|
|
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"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
|
|
5
|
+
__copyright__ = "2025-2026 Artur Barseghyan"
|
|
6
|
+
__license__ = "MIT"
|
|
7
|
+
__all__ = (
|
|
8
|
+
"CodeSnippet",
|
|
9
|
+
"group_snippets",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class CodeSnippet:
|
|
15
|
+
"""Data container for an extracted code snippet."""
|
|
16
|
+
code: str # The code content
|
|
17
|
+
line: int # Starting line number in the source
|
|
18
|
+
name: Optional[str] = None # Identifier for grouping (None if anonymous)
|
|
19
|
+
marks: list[str] = field(default_factory=list)
|
|
20
|
+
# Collected pytest marks (e.g. ['django_db']), parsed from doc comments
|
|
21
|
+
fixtures: list[str] = field(default_factory=list)
|
|
22
|
+
# Collected pytest fixtures (e.g. ['tmp_path']), parsed from doc comments
|
|
23
|
+
group: Optional[str] = None
|
|
24
|
+
# Set by ``continue:`` directives; names the group this snippet belongs to
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def group_snippets(snippets: list[CodeSnippet]) -> list[CodeSnippet]:
|
|
28
|
+
"""
|
|
29
|
+
Combine snippets that share a group key, using one of two modes:
|
|
30
|
+
|
|
31
|
+
- Merge mode (default): snippets sharing the same name (no ``group``
|
|
32
|
+
set, or nameless/same-name continuations) are concatenated into a single
|
|
33
|
+
test, accumulating marks and fixtures. This is the default behaviour.
|
|
34
|
+
- Incremental mode: when every continuation snippet (``group`` set) in
|
|
35
|
+
a group also carries its own distinct name, emit one test per snippet.
|
|
36
|
+
Each test's code is the cumulative concatenation of all preceding
|
|
37
|
+
snippets plus itself, so each step is exercised in isolation.
|
|
38
|
+
|
|
39
|
+
Unnamed snippets receive unique auto-keys so they are never merged.
|
|
40
|
+
"""
|
|
41
|
+
# Pass 1: bucket each snippet by its group key, preserving insertion order
|
|
42
|
+
buckets: dict[str, list[CodeSnippet]] = {}
|
|
43
|
+
order: list[str] = []
|
|
44
|
+
anon_count = 0
|
|
45
|
+
|
|
46
|
+
for sn in snippets:
|
|
47
|
+
if sn.group:
|
|
48
|
+
key = sn.group
|
|
49
|
+
elif sn.name:
|
|
50
|
+
key = sn.name
|
|
51
|
+
else:
|
|
52
|
+
anon_count += 1
|
|
53
|
+
key = f"codeblock{anon_count}"
|
|
54
|
+
|
|
55
|
+
if key not in buckets:
|
|
56
|
+
buckets[key] = []
|
|
57
|
+
order.append(key)
|
|
58
|
+
buckets[key].append(sn)
|
|
59
|
+
|
|
60
|
+
# Pass 2: emit merged or incremental snippets per bucket
|
|
61
|
+
combined: list[CodeSnippet] = []
|
|
62
|
+
|
|
63
|
+
for key in order:
|
|
64
|
+
members = buckets[key]
|
|
65
|
+
continuations = [sn for sn in members if sn.group]
|
|
66
|
+
# Incremental only when every continuation has a distinct own name
|
|
67
|
+
incremental = continuations and all(
|
|
68
|
+
sn.name and sn.name != key for sn in continuations
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if incremental:
|
|
72
|
+
acc_code = ""
|
|
73
|
+
acc_marks: list[str] = []
|
|
74
|
+
acc_fixtures: list[str] = []
|
|
75
|
+
for sn in members:
|
|
76
|
+
acc_code = acc_code + "\n" + sn.code if acc_code else sn.code
|
|
77
|
+
acc_marks.extend(sn.marks)
|
|
78
|
+
acc_fixtures.extend(sn.fixtures)
|
|
79
|
+
combined.append(CodeSnippet(
|
|
80
|
+
name=sn.name,
|
|
81
|
+
code=acc_code,
|
|
82
|
+
line=sn.line,
|
|
83
|
+
marks=list(acc_marks),
|
|
84
|
+
fixtures=list(acc_fixtures),
|
|
85
|
+
))
|
|
86
|
+
else:
|
|
87
|
+
# Merge mode (default behaviour)
|
|
88
|
+
first = members[0]
|
|
89
|
+
merged_marks = list(first.marks)
|
|
90
|
+
merged_fixtures = list(first.fixtures)
|
|
91
|
+
merged_code = first.code
|
|
92
|
+
for sn in members[1:]:
|
|
93
|
+
merged_code += "\n" + sn.code
|
|
94
|
+
merged_marks.extend(sn.marks)
|
|
95
|
+
merged_fixtures.extend(sn.fixtures)
|
|
96
|
+
combined.append(CodeSnippet(
|
|
97
|
+
name=first.name,
|
|
98
|
+
code=merged_code,
|
|
99
|
+
line=first.line,
|
|
100
|
+
marks=merged_marks,
|
|
101
|
+
fixtures=merged_fixtures,
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
return combined
|
|
@@ -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
|
-
#
|
|
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,35 +118,34 @@ def parse_markdown(text: str) -> list[CodeSnippet]:
|
|
|
118
118
|
break
|
|
119
119
|
if snippet_name is None:
|
|
120
120
|
snippet_name = pending_name
|
|
121
|
-
#
|
|
121
|
+
# Reset pending_name; marks stay until block closes
|
|
122
122
|
pending_name = None
|
|
123
|
-
continue
|
|
124
123
|
|
|
125
124
|
else:
|
|
126
|
-
#
|
|
125
|
+
# Inside a fenced code block
|
|
127
126
|
if line.lstrip().startswith(fence):
|
|
128
|
-
#
|
|
127
|
+
# End of block
|
|
129
128
|
in_block = False
|
|
130
129
|
code_text = "\n".join(code_buffer)
|
|
131
|
-
|
|
130
|
+
snippet_group = None
|
|
131
|
+
# Continue overrides snippet_name for grouping
|
|
132
132
|
if pending_continue:
|
|
133
|
-
|
|
133
|
+
snippet_group = pending_continue
|
|
134
134
|
pending_continue = None
|
|
135
|
-
else:
|
|
136
|
-
final_name = snippet_name
|
|
137
135
|
snippets.append(CodeSnippet(
|
|
138
|
-
name=
|
|
136
|
+
name=snippet_name,
|
|
139
137
|
code=code_text,
|
|
140
138
|
line=start_line,
|
|
141
139
|
marks=pending_marks.copy(),
|
|
142
140
|
fixtures=pending_fixtures.copy(),
|
|
141
|
+
group=snippet_group,
|
|
143
142
|
))
|
|
144
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
@@ -204,12 +206,12 @@ def parse_rst(text: str, base_dir: Path) -> list[CodeSnippet]:
|
|
|
204
206
|
k += 1
|
|
205
207
|
else:
|
|
206
208
|
break
|
|
209
|
+
sn_group = None
|
|
207
210
|
# Decide snippet name: continue overrides name_val/pending_name
|
|
208
211
|
if pending_continue:
|
|
209
|
-
|
|
212
|
+
sn_group = pending_continue
|
|
210
213
|
pending_continue = None
|
|
211
|
-
|
|
212
|
-
sn_name = name_val or pending_name
|
|
214
|
+
sn_name = name_val or pending_name
|
|
213
215
|
sn_marks = pending_marks.copy()
|
|
214
216
|
sn_fixtures = pending_fixtures.copy()
|
|
215
217
|
pending_name = None
|
|
@@ -222,6 +224,7 @@ def parse_rst(text: str, base_dir: Path) -> list[CodeSnippet]:
|
|
|
222
224
|
line=j + 1,
|
|
223
225
|
marks=sn_marks,
|
|
224
226
|
fixtures=sn_fixtures,
|
|
227
|
+
group=sn_group,
|
|
225
228
|
))
|
|
226
229
|
|
|
227
230
|
i = k
|
|
@@ -235,11 +238,11 @@ def parse_rst(text: str, base_dir: Path) -> list[CodeSnippet]:
|
|
|
235
238
|
# --------------------------------------------------------------------
|
|
236
239
|
if line.rstrip().endswith("::") and pending_name:
|
|
237
240
|
# Similar override logic
|
|
241
|
+
sn_group = None
|
|
238
242
|
if pending_continue:
|
|
239
|
-
|
|
243
|
+
sn_group = pending_continue
|
|
240
244
|
pending_continue = None
|
|
241
|
-
|
|
242
|
-
sn_name = pending_name
|
|
245
|
+
sn_name = pending_name
|
|
243
246
|
sn_marks = pending_marks.copy()
|
|
244
247
|
sn_fixtures = pending_fixtures.copy()
|
|
245
248
|
pending_name = None
|
|
@@ -273,6 +276,7 @@ def parse_rst(text: str, base_dir: Path) -> list[CodeSnippet]:
|
|
|
273
276
|
line=j + 1,
|
|
274
277
|
marks=sn_marks,
|
|
275
278
|
fixtures=sn_fixtures,
|
|
279
|
+
group=sn_group,
|
|
276
280
|
))
|
|
277
281
|
i = k
|
|
278
282
|
continue
|
{pytest_codeblock-0.5.5 → pytest_codeblock-0.5.7}/src/pytest_codeblock/tests/test_customisation.py
RENAMED
|
@@ -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."""
|