mktestdocs 0.2.3__tar.gz → 0.2.5__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.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: mktestdocs
3
+ Version: 0.2.5
4
+ Summary: A tool for testing markdown documentation
5
+ License: MIT
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Provides-Extra: test
10
+ Requires-Dist: pytest; extra == "test"
11
+ Dynamic: license-file
12
+
13
+ <img src="icon.png" width=125 height=125 align="right">
14
+
15
+ ### mktestdocs
16
+
17
+ Run pytest against markdown files/docstrings.
18
+
19
+ # Installation
20
+
21
+ ```
22
+ pip install mktestdocs
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ Let's say that you're using [mkdocs](https://squidfunk.github.io/mkdocs-material/getting-started/)
28
+ for your documentation. Then you're writing down markdown to explain how your Python packages work.
29
+ It'd be a shame if a codeblock had an error in it, so it'd be
30
+ great if you could run your unit tests against them.
31
+
32
+ This package allows you to do _just that_. Here's an example:
33
+
34
+ ```python
35
+ import pathlib
36
+ import pytest
37
+
38
+ from mktestdocs import check_md_file
39
+
40
+ # Note the use of `str`, makes for pretty output
41
+ @pytest.mark.parametrize('fpath', pathlib.Path("docs").glob("**/*.md"), ids=str)
42
+ def test_files_good(fpath):
43
+ check_md_file(fpath=fpath)
44
+ ```
45
+
46
+ This will take any codeblock that starts with *\`\`\`python* and run it, checking
47
+ for any errors that might happen. This means that if your docs contain asserts, that
48
+ you get some unit-tests for free!
49
+
50
+ ## Multiple Code Blocks
51
+
52
+ Let's suppose that you have the following markdown file:
53
+
54
+ This is a code block
55
+
56
+ ```python
57
+ from operator import add
58
+ a = 1
59
+ b = 2
60
+ ```
61
+
62
+ This code-block should run fine.
63
+
64
+ ```python
65
+ assert add(1, 2) == 3
66
+ ```
67
+
68
+ Then in this case the second code-block depends on the first code-block. The standard settings of `check_md_file` assume that each code-block needs to run independently. If you'd like to test markdown files with these sequential code-blocks be sure to set `memory=True`.
69
+
70
+ ```python
71
+ import pathlib
72
+
73
+ from mktestdocs import check_md_file
74
+
75
+ fpath = pathlib.Path("docs") / "multiple-code-blocks.md"
76
+
77
+ try:
78
+ # Assume that cell-blocks are independent.
79
+ check_md_file(fpath=fpath)
80
+ except NameError:
81
+ # But they weren't
82
+ pass
83
+
84
+ # Assumes that cell-blocks depend on each other.
85
+ check_md_file(fpath=fpath, memory=True)
86
+ ```
87
+
88
+ ## Markdown in Docstrings
89
+
90
+ You might also have docstrings written in markdown. Those can be easily checked
91
+ as well.
92
+
93
+ ```python
94
+ # I'm assuming that we've got a library called dinosaur
95
+ from dinosaur import roar, super_roar
96
+
97
+ import pytest
98
+ from mktestdocs import check_docstring
99
+
100
+ # Note the use of `__name__`, makes for pretty output
101
+ @pytest.mark.parametrize('func', [roar, super_roar], ids=lambda d: d.__name__)
102
+ def test_docstring(func):
103
+ check_docstring(obj=func)
104
+ ```
105
+
106
+ There's even some utilities for grab all the docstrings from classes that you've defined.
107
+
108
+ ```python
109
+ # I'm assuming that we've got a library called dinosaur
110
+ from dinosaur import Dinosaur
111
+
112
+ import pytest
113
+ from mktestdocs import check_docstring, get_codeblock_members
114
+
115
+ # This retrieves all methods/properties that have a docstring.
116
+ members = get_codeblock_members(Dinosaur)
117
+
118
+ # Note the use of `__qualname__`, makes for pretty output
119
+ @pytest.mark.parametrize("obj", members, ids=lambda d: d.__qualname__)
120
+ def test_member(obj):
121
+ check_docstring(obj)
122
+ ```
123
+
124
+ When you run these commands via `pytest --verbose` you should see informative test info being run.
125
+
126
+ If you're wondering why you'd want to write markdown in a docstring feel free to check out [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings).
127
+
128
+ ## Bash Support
129
+
130
+ Be default, bash code blocks are also supported. A markdown file that contains
131
+ both python and bash code blocks can have each executed separately.
132
+
133
+ This will print the python version to the terminal
134
+
135
+ ```bash
136
+ python --version
137
+ ```
138
+
139
+ This will print the exact same version string
140
+
141
+ ```python
142
+ import sys
143
+
144
+ print(f"Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
145
+ ```
146
+
147
+ This markdown could be fully tested like this
148
+
149
+ ```python
150
+ import pathlib
151
+
152
+ from mktestdocs import check_md_file
153
+
154
+ fpath = pathlib.Path("docs") / "bash-support.md"
155
+
156
+ check_md_file(fpath=fpath, lang="python")
157
+ check_md_file(fpath=fpath, lang="bash")
158
+ ```
159
+
160
+ ## Additional Language Support
161
+
162
+ You can add support for languages other than python and bash by first
163
+ registering a new executor for that language. The `register_executor` function
164
+ takes a tag to specify the code block type supported, and a function that will
165
+ be passed any code blocks found in markdown files.
166
+
167
+ For example if you have a markdown file like this
168
+
169
+ ````markdown
170
+ This is an example REST response
171
+
172
+ ```json
173
+ {"body": {"results": ["spam", "eggs"]}, "errors": []}
174
+ ```
175
+ ````
176
+
177
+ You could create a json validator that tested the example was always valid json like this
178
+
179
+ ```python
180
+ import json
181
+ import pathlib
182
+
183
+ from mktestdocs import check_md_file, register_executor
184
+
185
+ def parse_json(json_text):
186
+ json.loads(json_text)
187
+
188
+ register_executor("json", parse_json)
189
+
190
+ check_md_file(fpath=pathlib.Path("docs") / "additional-language-support.md", lang="json")
191
+ ```
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mktestdocs"
7
+ version = "0.2.5"
8
+ description = "A tool for testing markdown documentation"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.8"
12
+ dependencies = []
13
+
14
+ [project.optional-dependencies]
15
+ test = ["pytest"]
16
+
17
+ [tool.setuptools.packages.find]
18
+ where = ["src"]
@@ -1,4 +1,5 @@
1
- from mktestdocs.__main__ import (
1
+ import importlib.metadata
2
+ from .__main__ import (
2
3
  register_executor,
3
4
  check_codeblock,
4
5
  grab_code_blocks,
@@ -7,7 +8,7 @@ from mktestdocs.__main__ import (
7
8
  get_codeblock_members,
8
9
  )
9
10
 
10
- __version__ = "0.2.3"
11
+ __version__ = importlib.metadata.version("mktestdocs")
11
12
 
12
13
  __all__ = [
13
14
  "__version__",
@@ -40,7 +40,7 @@ def exec_python(source):
40
40
  will propagate out unmodified
41
41
  """
42
42
  try:
43
- exec(source, {"__MODULE__": "__main__"})
43
+ exec(source, {"__name__": "__main__"})
44
44
  except Exception:
45
45
  print(source)
46
46
  raise
@@ -50,7 +50,7 @@ register_executor("", exec_python)
50
50
  register_executor("python", exec_python)
51
51
 
52
52
 
53
- def get_codeblock_members(*classes):
53
+ def get_codeblock_members(*classes, lang="python"):
54
54
  """
55
55
  Grabs the docstrings of any methods of any classes that are passed in.
56
56
  """
@@ -61,7 +61,7 @@ def get_codeblock_members(*classes):
61
61
  for name, member in inspect.getmembers(cl):
62
62
  if member.__doc__:
63
63
  results.append(member)
64
- return [m for m in results if len(grab_code_blocks(m.__doc__)) > 0]
64
+ return [m for m in results if len(grab_code_blocks(m.__doc__, lang=lang)) > 0]
65
65
 
66
66
 
67
67
  def check_codeblock(block, lang="python"):
@@ -76,7 +76,7 @@ def check_codeblock(block, lang="python"):
76
76
  """
77
77
  first_line = block.split("\n")[0]
78
78
  if lang:
79
- if first_line[3:] != lang:
79
+ if first_line.lstrip()[3:] != lang:
80
80
  return ""
81
81
  return "\n".join(block.split("\n")[1:])
82
82
 
@@ -104,12 +104,14 @@ def grab_code_blocks(docstring, lang="python"):
104
104
  block += line + "\n"
105
105
  return [textwrap.dedent(c) for c in codeblocks if c != ""]
106
106
 
107
+
107
108
  def format_docstring(docstring):
108
109
  """Formats docstring to be able to successfully go through dedent."""
109
110
  if docstring[:1] != "\n":
110
111
  return f"\n {docstring}"
111
112
  return docstring
112
113
 
114
+
113
115
  def check_docstring(obj, lang=""):
114
116
  """
115
117
  Given a function, test the contents of the docstring.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: mktestdocs
3
+ Version: 0.2.5
4
+ Summary: A tool for testing markdown documentation
5
+ License: MIT
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Provides-Extra: test
10
+ Requires-Dist: pytest; extra == "test"
11
+ Dynamic: license-file
12
+
13
+ <img src="icon.png" width=125 height=125 align="right">
14
+
15
+ ### mktestdocs
16
+
17
+ Run pytest against markdown files/docstrings.
18
+
19
+ # Installation
20
+
21
+ ```
22
+ pip install mktestdocs
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ Let's say that you're using [mkdocs](https://squidfunk.github.io/mkdocs-material/getting-started/)
28
+ for your documentation. Then you're writing down markdown to explain how your Python packages work.
29
+ It'd be a shame if a codeblock had an error in it, so it'd be
30
+ great if you could run your unit tests against them.
31
+
32
+ This package allows you to do _just that_. Here's an example:
33
+
34
+ ```python
35
+ import pathlib
36
+ import pytest
37
+
38
+ from mktestdocs import check_md_file
39
+
40
+ # Note the use of `str`, makes for pretty output
41
+ @pytest.mark.parametrize('fpath', pathlib.Path("docs").glob("**/*.md"), ids=str)
42
+ def test_files_good(fpath):
43
+ check_md_file(fpath=fpath)
44
+ ```
45
+
46
+ This will take any codeblock that starts with *\`\`\`python* and run it, checking
47
+ for any errors that might happen. This means that if your docs contain asserts, that
48
+ you get some unit-tests for free!
49
+
50
+ ## Multiple Code Blocks
51
+
52
+ Let's suppose that you have the following markdown file:
53
+
54
+ This is a code block
55
+
56
+ ```python
57
+ from operator import add
58
+ a = 1
59
+ b = 2
60
+ ```
61
+
62
+ This code-block should run fine.
63
+
64
+ ```python
65
+ assert add(1, 2) == 3
66
+ ```
67
+
68
+ Then in this case the second code-block depends on the first code-block. The standard settings of `check_md_file` assume that each code-block needs to run independently. If you'd like to test markdown files with these sequential code-blocks be sure to set `memory=True`.
69
+
70
+ ```python
71
+ import pathlib
72
+
73
+ from mktestdocs import check_md_file
74
+
75
+ fpath = pathlib.Path("docs") / "multiple-code-blocks.md"
76
+
77
+ try:
78
+ # Assume that cell-blocks are independent.
79
+ check_md_file(fpath=fpath)
80
+ except NameError:
81
+ # But they weren't
82
+ pass
83
+
84
+ # Assumes that cell-blocks depend on each other.
85
+ check_md_file(fpath=fpath, memory=True)
86
+ ```
87
+
88
+ ## Markdown in Docstrings
89
+
90
+ You might also have docstrings written in markdown. Those can be easily checked
91
+ as well.
92
+
93
+ ```python
94
+ # I'm assuming that we've got a library called dinosaur
95
+ from dinosaur import roar, super_roar
96
+
97
+ import pytest
98
+ from mktestdocs import check_docstring
99
+
100
+ # Note the use of `__name__`, makes for pretty output
101
+ @pytest.mark.parametrize('func', [roar, super_roar], ids=lambda d: d.__name__)
102
+ def test_docstring(func):
103
+ check_docstring(obj=func)
104
+ ```
105
+
106
+ There's even some utilities for grab all the docstrings from classes that you've defined.
107
+
108
+ ```python
109
+ # I'm assuming that we've got a library called dinosaur
110
+ from dinosaur import Dinosaur
111
+
112
+ import pytest
113
+ from mktestdocs import check_docstring, get_codeblock_members
114
+
115
+ # This retrieves all methods/properties that have a docstring.
116
+ members = get_codeblock_members(Dinosaur)
117
+
118
+ # Note the use of `__qualname__`, makes for pretty output
119
+ @pytest.mark.parametrize("obj", members, ids=lambda d: d.__qualname__)
120
+ def test_member(obj):
121
+ check_docstring(obj)
122
+ ```
123
+
124
+ When you run these commands via `pytest --verbose` you should see informative test info being run.
125
+
126
+ If you're wondering why you'd want to write markdown in a docstring feel free to check out [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings).
127
+
128
+ ## Bash Support
129
+
130
+ Be default, bash code blocks are also supported. A markdown file that contains
131
+ both python and bash code blocks can have each executed separately.
132
+
133
+ This will print the python version to the terminal
134
+
135
+ ```bash
136
+ python --version
137
+ ```
138
+
139
+ This will print the exact same version string
140
+
141
+ ```python
142
+ import sys
143
+
144
+ print(f"Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
145
+ ```
146
+
147
+ This markdown could be fully tested like this
148
+
149
+ ```python
150
+ import pathlib
151
+
152
+ from mktestdocs import check_md_file
153
+
154
+ fpath = pathlib.Path("docs") / "bash-support.md"
155
+
156
+ check_md_file(fpath=fpath, lang="python")
157
+ check_md_file(fpath=fpath, lang="bash")
158
+ ```
159
+
160
+ ## Additional Language Support
161
+
162
+ You can add support for languages other than python and bash by first
163
+ registering a new executor for that language. The `register_executor` function
164
+ takes a tag to specify the code block type supported, and a function that will
165
+ be passed any code blocks found in markdown files.
166
+
167
+ For example if you have a markdown file like this
168
+
169
+ ````markdown
170
+ This is an example REST response
171
+
172
+ ```json
173
+ {"body": {"results": ["spam", "eggs"]}, "errors": []}
174
+ ```
175
+ ````
176
+
177
+ You could create a json validator that tested the example was always valid json like this
178
+
179
+ ```python
180
+ import json
181
+ import pathlib
182
+
183
+ from mktestdocs import check_md_file, register_executor
184
+
185
+ def parse_json(json_text):
186
+ json.loads(json_text)
187
+
188
+ register_executor("json", parse_json)
189
+
190
+ check_md_file(fpath=pathlib.Path("docs") / "additional-language-support.md", lang="json")
191
+ ```
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/mktestdocs/__init__.py
5
+ src/mktestdocs/__main__.py
6
+ src/mktestdocs.egg-info/PKG-INFO
7
+ src/mktestdocs.egg-info/SOURCES.txt
8
+ src/mktestdocs.egg-info/dependency_links.txt
9
+ src/mktestdocs.egg-info/requires.txt
10
+ src/mktestdocs.egg-info/top_level.txt
11
+ tests/test_actual_docstrings.py
12
+ tests/test_class.py
13
+ tests/test_codeblock.py
14
+ tests/test_format_docstring.py
15
+ tests/test_markdown.py
16
+ tests/test_mktestdocs.py
@@ -0,0 +1,3 @@
1
+
2
+ [test]
3
+ pytest
@@ -60,12 +60,27 @@ class Dinosaur:
60
60
  """
61
61
  return self.name
62
62
 
63
+ def hfdocs_style(self, value):
64
+ """
65
+ Returns value
66
+
67
+ Example:
68
+
69
+ ```python
70
+ from dinosaur import Dinosaur
71
+
72
+ dino = Dinosaur()
73
+ assert dino.a(1) == 1
74
+ ```
75
+ """
76
+ return value
77
+
63
78
 
64
79
  members = get_codeblock_members(Dinosaur)
65
80
 
66
81
 
67
82
  def test_grab_methods():
68
- assert len(get_codeblock_members(Dinosaur)) == 4
83
+ assert len(get_codeblock_members(Dinosaur)) == 5
69
84
 
70
85
 
71
86
  @pytest.mark.parametrize("obj", members, ids=lambda d: d.__qualname__)
@@ -2,9 +2,10 @@ import pathlib
2
2
 
3
3
  from mktestdocs import check_md_file
4
4
 
5
+
5
6
  def test_readme(monkeypatch):
6
7
  test_dir = pathlib.Path(__file__).parent
7
8
  fpath = test_dir.parent / "README.md"
8
9
  monkeypatch.chdir(test_dir)
9
10
 
10
- check_md_file(fpath=fpath)
11
+ check_md_file(fpath=fpath, memory=True)
mktestdocs-0.2.3/PKG-INFO DELETED
@@ -1,6 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: mktestdocs
3
- Version: 0.2.3
4
- License-File: LICENSE
5
- Provides-Extra: test
6
- Requires-Dist: pytest>=4.0.2; extra == "test"
@@ -1,6 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: mktestdocs
3
- Version: 0.2.3
4
- License-File: LICENSE
5
- Provides-Extra: test
6
- Requires-Dist: pytest>=4.0.2; extra == "test"
@@ -1,16 +0,0 @@
1
- LICENSE
2
- README.md
3
- setup.py
4
- mktestdocs/__init__.py
5
- mktestdocs/__main__.py
6
- mktestdocs.egg-info/PKG-INFO
7
- mktestdocs.egg-info/SOURCES.txt
8
- mktestdocs.egg-info/dependency_links.txt
9
- mktestdocs.egg-info/requires.txt
10
- mktestdocs.egg-info/top_level.txt
11
- tests/test_actual_docstrings.py
12
- tests/test_class.py
13
- tests/test_codeblock.py
14
- tests/test_format_docstring.py
15
- tests/test_markdown.py
16
- tests/test_mktestdocs.py
@@ -1,3 +0,0 @@
1
-
2
- [test]
3
- pytest>=4.0.2
mktestdocs-0.2.3/setup.py DELETED
@@ -1,14 +0,0 @@
1
- from mktestdocs import __version__
2
- from setuptools import setup, find_packages
3
-
4
- test_packages = ["pytest>=4.0.2"]
5
-
6
- setup(
7
- name="mktestdocs",
8
- version=__version__,
9
- packages=find_packages(exclude=["tests"]),
10
- install_requires=[],
11
- extras_require={
12
- "test": test_packages,
13
- },
14
- )
File without changes
File without changes
File without changes