-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_tutorial.py
332 lines (246 loc) · 10.8 KB
/
test_tutorial.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
"""
Creates Readme - while testing functions of p2m.
While pytest is running we simply assemble from scratch an intermediate .md file
in append only mode, located within the tests folder.
This we insert between two seperators in the target markdown file, as the last
test function, done.
"""
import pytest2md
import pytest, json, os, time
from functools import partial
from uuid import uuid4
import json
import inspect
# a P2M instance contains all we need:
p2m = pytest2md.P2M(__file__, fn_target_md='README.md')
# parametrizing the shell run results:
run = partial(p2m.bash_run, cmd_path_from_env=True)
doc_src = partial(
p2m.md_from_source_code,
pre_md='\n----\nGenerated by:',
no_sh_func_output=True,
)
md = p2m.md
class TestChapter1:
def test_one(self):
t = """
This is a set of tools, *generating* documentation, while verifying the documented
claims about code behaviour - without the need to adapt the source code, e.g. by modifying
doc strings:
![](./assets/shot1.png)
> When the documentation is using a lot of code examples then a very welcome
additional benefit of writing it like shown is the availability of [source
code autoformatters](https://github.com/ambv/black).
Other Example:
This "README.md" was built into [this](./.README.tmpl.md) template,
where [title:html comment style placeholders,fmatch:.README.tmpl.md,show_raw:True]<SRC>
had been replaced while running pytest on [test_tutorial](./tests/test_tutorial.py).
"""
md(t)
md(
"""
Lets run a bash command and assert on its results.
Note that the command is shown in the readme, incl. result and the result
can be asserted upon.
"""
)
res = run('cat "/etc/hosts" | grep localhost')
assert '127.0.0.1' in res[0]['res']
def test_two(self):
res = run(['ls "%(d_test)s"' % p2m.ctx, 'ls -lta /etc/hosts'])
assert 'tutorial' in res[0]['res']
assert 'hosts' in res[1]['res']
def test_simple_pyfuncs(self):
"""
# Inline Python Function Execution
via the `md_from_source_code` function you can write fluent markdown
(tested) python combos:
"""
def foo():
hi = 'hello world'
assert 'world' in hi
print(hi.replace('world', 'joe'))
"""
The functions are evaluated and documented in the order they show up
within the textblocks.
> Please keep tripple apostrophes - we split the text blocks,
searching for those.
State is kept within the outer pytest function, like normally in python.
I.e. if you require new state, then start a new pytest function.
Stdout is redirected to an output collector function, i.e. if you print
this does result in an "Output" block. If the printout starts with
"MARKDOWN:" then we won't wrap that output into fenced code blocks but
display as is.
> If the string 'breakpoint' occurs in a function body, we won't redirect
standardout for displaying output.
# Features
## html_table
"""
def mdtable():
ht = p2m.html_table
print(ht([['foo', 'bar'], ['bar', 'baz']], ['h1', 'h2']))
print('As details when summary arg is given:')
t = ht(
[['joe', 'doe'], ['suzie', 'wong']],
['first', 'last'],
summary='names. click to open...',
)
assert 'details' in t
assert 'joe</td' in t
print(t)
doc_src()
def test_file_create_show(self):
md('## sh_file')
fn = '/tmp/test_file.json'
ts = time.ctime()
c = json.dumps({'a': [{'testfile': 'created'}, 'at', ts]}, indent=4)
def shf():
# if content is given it will create it:
p2m.sh_file(fn, lang='javascript', content=c)
# summary arg creates a details structure:
p2m.sh_file(fn, lang='javascript', content=c, summary='as details')
# creates a link (say True to have the filename as link text)
p2m.sh_file(fn, lang='javascript', content=c, as_link=True)
p2m.sh_file(fn, lang='javascript', content=c, as_link='as link')
doc_src()
# check existance:
with open(fn) as fd:
assert fd.read() == c
os.unlink(fn)
def test_bash_run(self):
md('## bash_run')
def br():
run = partial(p2m.bash_run, cmd_path_from_env=True)
# by default we search in normal environ for the command to run
# but we provide a switch to search in test assets.
# errors are redirected to stdout
res = p2m.bash_run(
'some_non_existing_command_in_assets arg1',
cmd_path_from_env=False,
ign_err=True,
)
assert res[0]['exitcode'] != 0
res = run('ls -lta | grep total | head -n 1')
assert 'total' in res[0]['res']
res = run('ls -lta', into_file='bash_run.txt')
assert 'total' in res[0]['res']
# Ending with .html it converts ansi escape colors to html:
# simple link is created.
# (requires pip install ansi2html)
run('ls -lta', into_file='bash_run.html')
doc_src()
def test_md_from_source_code(self):
md('## md_from_source_code')
def some_test_function():
"""
Strings in double apos. are rendered, no need to call a render function.
"""
def func1():
md('Inserted markdown from running python.')
print('From output of running python ')
"""
> More markdown
"""
def func2():
print('From another function')
"""
Strings can also contain instructions, like this (looked up in p2m.MdInline namespace class)
<bash: cd MY_REPL_DIR; ls -lta | head -n 5>
Default inline functions (add your own in module headers):
"""
def known():
print(
[
k
for k in dir(pytest2md.MdInline)
if not k.startswith('_')
]
)
# repl dict simply replaces keys with values before any processing:
p2m.md_from_source_code(repl={'MY_REPL_DIR': '/etc'})
some_test_function()
assert '$ cd /etc' in p2m.ctx['md'][-1]
assert 'total' in p2m.ctx['md'][-1]
l = [
'----',
'Generated by',
'```python',
inspect.getsource(some_test_function),
'```',
]
p2m.ctx['md'].append('\n'.join(l))
def test_toc_making(self):
md(
"""
## Table of Contents
p2m.write_markdown(with_source_ref=True, make_toc=True)
See this tutorial.
"""
)
def test_mdtool(self):
t = """
# Link Replacements
Technical markdown content wants to link to source code often.
How to get those links working and that convenient?
The module does offer also some source finding / link replacement feature,
via the [mdtool]<SRC> module. The latter link was built simply by this:
```
[mdtool]<SRC>
```
Other example: This [test_tutorial.py]<SRC> link was created by replacing "SRC" with the path
to a file matching, under a given directory, prefixed by an arbitrary base URL.
## Spec
These will be replaced:
`[title:this,fmatch:test_tutorial,lmatch:line_match] <SRC>` (remove space between] and <)
- title: The link title - text the user reads
- fmatch: substring match for the link destination file
- lmatch: Find matching line within that file
- show_raw: Link to raw version of file, not the one rendered by the
repo server
- path: Fix file path (usually derived by fmach)
- line: Fix the line number of the link (usually done via lmatch)
## Code Repo Hoster Specific Source Links
Github, Gitlab, Bitbucked or Plain directory based static content servers
all have their conventional URLs regarding those links.
Since all of these are just serving static content w/o js possibilities,
you have to parametrize the intended hoster in your environment, before
running a pytest / push cycle. That way the links will be working on the hoster.
Currently we understand the following namespaces for links:
```javascript
_link_names_
```
### Setting a link template
- `export MD_LINKS_FOR=github ` # before running pytest / push
- `<!-- md_links_for: github -->` # in the markdown template, static
The latter can be overwritten by environ, should you want to push from time to time
to a different code hoster.
### Link Refs
We minimize the problem of varying generated target markdown, dependent on the hoster.
How? Like [any problem in IT is solved](https://en.wikipedia.org/wiki/Fundamental_theorem_of_software_engineering).
By building [reference links](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#links)
the differences of e.g. a README.md for github vs. gitlab is
restricted to the links section on the end of the generated markdown.
In the markdown bodies you'll just see link names, which remain the same.
> Check the end of the [title:rendering result,fmatch:README.md,show_raw:True]<SRC> at the end of this README.md,
in order to see the results for the hoster you are reading this markdown file currently.
## Summary
- At normal runs of pytest, the link base URL is just a local `file://` link,
- Before pushes one can set via environ (e.g. `export
MD_LINKS_FOR=github`) these e.g. to the github base URL or the repo.
- `[key-values]` constructs are supported as well, extending to beyond
just the base url. Example following:
""".replace(
'_link_names_',
json.dumps(p2m.src_link_templates, indent=4, sort_keys=2),
)
md(t)
def test_sh_code(self):
md('Source code showing is done like this:')
p2m.sh_code(self.test_sh_code)
md(
'> Is [title:this,fmatch:test_tutorial,lmatch:exotic]<SRC> an exotic form of a recursion? ;-) '
)
def test_write(self):
"""has to be the last 'test' - or done by a fixture"""
# default is ../README.md
p2m.write_markdown(with_source_ref=True, make_toc=True)