Skip to content

Commit 4c90c2b

Browse files
0.3
- Added some more documentation and fixed some false path warnings
1 parent b08b984 commit 4c90c2b

File tree

7 files changed

+153
-44
lines changed

7 files changed

+153
-44
lines changed

doc/description.rst

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@ Usage
44

55
Without options
66
------------------------------
7-
.. code-block:: rst
7+
.. code-block:: python
88
99
.. exec_code::
1010
1111
print('Easy!')
1212
13-
Generated view:
13+
Generated view
14+
15+
----
1416

1517
.. exec_code::
1618

1719
print('Easy!')
1820

21+
----
22+
1923
Options
2024
------------------------------
2125
It's possible to further configure both the code block and the output block with the following options:
@@ -34,7 +38,7 @@ language/language_output:
3438

3539
Example:
3640

37-
.. code-block:: rst
41+
.. code-block:: python
3842
3943
.. exec_code::
4044
:linenos:
@@ -43,7 +47,9 @@ Example:
4347
4448
print('Easy!')
4549
46-
Generated view:
50+
Generated view
51+
52+
----
4753

4854
.. exec_code::
4955
:linenos:
@@ -52,12 +58,15 @@ Generated view:
5258

5359
print('Easy!')
5460

61+
----
62+
5563

56-
Hide code parts
64+
Code Markers
5765
------------------------------
5866
It's possible to hide parts of the code (e.g. to setup a working example)
5967
and it's possible to skip part of the code execution. This is possible with the
6068
``#hide:[start|stop|toggle]`` or ``#skip:[start|stop|toggle]`` marker in the code.
69+
Empty lines after a disabling marker will be ignored.
6170

6271
Spaces and dashes are ignored for the case insensitive marker detection so these are all the same:
6372

@@ -69,33 +78,75 @@ Spaces and dashes are ignored for the case insensitive marker detection so these
6978
# ----- hide: start -----
7079
7180
72-
Example:
73-
74-
.. code-block:: rst
81+
Hiding code parts
82+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
83+
.. code-block:: python
7584
7685
.. exec_code::
7786
7887
# --- hide: start ---
7988
print('Setup!')
8089
#hide:toggle
90+
8191
print('Easy!')
92+
8293
# --- hide: start ---
8394
print('Hidden!')
8495
# --- hide: stop ---
96+
8597
# Note the missing entries!
8698
print('Visible!')
8799
88100
89-
Generated view:
101+
Generated view (note the skipped empty lines after the stop and disabling toggle marker)
102+
103+
----
90104

91105
.. exec_code::
92106

93107
# --- hide: start ---
94108
print('Setup!')
95109
#hide:toggle
110+
96111
print('Easy!')
112+
97113
# --- hide: start ---
98114
print('Hidden!')
99115
# --- hide: stop ---
116+
100117
# Note the missing entries!
101118
print('Visible!')
119+
120+
----
121+
122+
Skipping code parts
123+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124+
.. code-block:: python
125+
126+
.. exec_code::
127+
128+
# --- skip: start ---
129+
print(f'1 / 0 = {1 / 0}')
130+
# --- skip: stop ---
131+
132+
# --- hide: start ---
133+
print('1 / 0 = 0')
134+
# --- hide: stop ---
135+
136+
Generated view
137+
138+
----
139+
140+
.. exec_code::
141+
142+
# --- skip: start ---
143+
print(f'1 / 0 = {1 / 0}')
144+
# --- skip: stop ---
145+
146+
# --- hide: start ---
147+
print('1 / 0 = 0')
148+
# --- hide: stop ---
149+
150+
----
151+
152+
With the combination of ``skip`` and ``hide`` it's possible to "simulate" every code.

readme.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ Sphinx-exec-code allows execution of any python code during the documentation bu
1212
It's also possible to display the output of the code execution.
1313

1414
With this extension it's easy to ensure that the provided code samples are always working.
15-
Additionally, it's possible to inspect and print output of the documented code.
15+
Additionally, it's possible to show the output of the documented code.
16+
17+
Each code snippet runs in a fresh interpreter so changes to not leak between executions.
1618

1719
## Documentation
1820
[The full documentation can be found at here](https://sphinx-exec-code.readthedocs.io)
@@ -22,12 +24,20 @@ Additionally, it's possible to inspect and print output of the documented code.
2224

2325
````text
2426
.. exec-code::
25-
:hide_output:
2627
2728
print('This code will be executed')
2829
````
29-
30+
generates
31+
```python
32+
print('This code will be executed')
33+
```
34+
```
35+
This code will be executed
36+
```
3037

3138
# Changelog
39+
#### 0.3 (24.09.2021)
40+
- Added some more documentation and fixed some false path warnings
41+
3242
#### 0.2 (21.09.2021)
3343
- Initial Release

src/sphinx_exec_code/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.2'
1+
__version__ = '0.3'

src/sphinx_exec_code/code_format.py

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,73 @@
1-
from typing import Iterable, List, Tuple
1+
from typing import Iterable, Tuple
22

33

44
class VisibilityMarkerError(Exception):
55
pass
66

77

8-
def _process_code(code_lines: Iterable[str], marker: str) -> List[str]:
8+
class CodeMarker:
9+
MARKERS = ('hide', 'skip')
910

10-
marker_start = f'#{marker}:start'
11-
marker_stop = f'#{marker}:stop'
12-
marker_toggle = f'#{marker}:toggle'
11+
def __init__(self, marker: str):
12+
assert marker in CodeMarker.MARKERS
13+
self.start = f'#{marker}:start'
14+
self.stop = f'#{marker}:stop'
15+
self.toggle = f'#{marker}:toggle'
16+
17+
self.do_add = True
18+
self.skip_empty = False
19+
self.lines = []
20+
21+
def is_marker(self, line: str) -> bool:
22+
if line == self.start:
23+
if not self.do_add:
24+
raise VisibilityMarkerError(f'{self.start[1:]} already called! '
25+
f'Use {self.stop[1:]} or {self.toggle[1:]}!')
26+
self.do_add = False
27+
return True
28+
29+
if line == self.stop:
30+
if self.do_add:
31+
raise VisibilityMarkerError(f'{self.stop[1:]} already called! '
32+
f'Use {self.start[1:]} or {self.toggle[1:]}!')
33+
self.do_add = True
34+
self.skip_empty = True
35+
return True
36+
37+
if line == self.toggle:
38+
self.do_add = not self.do_add
39+
return True
40+
41+
return False
42+
43+
def add_line(self, line: str):
44+
if not self.do_add:
45+
return None
46+
47+
if self.skip_empty:
48+
if not line.strip():
49+
return None
50+
self.skip_empty = False
51+
52+
self.lines.append(line)
53+
54+
55+
def get_show_exec_code(code_lines: Iterable[str]) -> Tuple[str, str]:
56+
hide = CodeMarker('hide')
57+
skip = CodeMarker('skip')
1358

14-
lines = []
15-
do_add = True
1659
for org_line in code_lines:
1760
line = org_line.replace(' ', '').replace('-', '').lower()
18-
if line == marker_start:
19-
if not do_add:
20-
raise VisibilityMarkerError(f'{marker}:start already called! Use {marker}:stop or {marker}:toggle!')
21-
do_add = False
22-
continue
23-
elif line == marker_stop:
24-
if do_add:
25-
raise VisibilityMarkerError(f'{marker}:stop already called! Use {marker}:start or {marker}:toggle!')
26-
do_add = True
61+
62+
if hide.is_marker(line):
2763
continue
28-
elif line == marker_toggle:
29-
do_add = not do_add
64+
if skip.is_marker(line):
3065
continue
3166

32-
if do_add:
33-
lines.append(org_line)
34-
return lines
35-
67+
hide.add_line(org_line)
68+
skip.add_line(org_line)
3669

37-
def get_show_exec_code(code_lines: Iterable[str]) -> Tuple[str, str]:
38-
shown_code = '\n'.join(_process_code(code_lines, 'hide'))
39-
executed_code = '\n'.join(_process_code(code_lines, 'skip'))
70+
shown_code = '\n'.join(hide.lines)
71+
executed_code = '\n'.join(skip.lines)
4072

4173
return shown_code.strip(), executed_code.strip()

src/sphinx_exec_code/sphinx_api.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,23 @@ def builder_ready(app):
3232
raise FileNotFoundError(f'Additional directory "{_f}" not found! (configured by {CONF_NAME_DIRS})')
3333

3434
# Search for a python package and print a warning if we find none
35-
# since this is the only reason to specify a working dir
35+
# since this is the only reason to specify additional folders
3636
for _f in folders:
37+
package_found = False
3738
for __f in _f.iterdir():
3839
if not __f.is_dir():
3940
continue
4041

4142
# log warning if we don't find a python package
4243
for file in __f.iterdir():
4344
if file.name == '__init__.py':
45+
package_found = True
4446
break
45-
else:
46-
log.warning(f'[exec-code] No Python package found in {_f}')
47+
if package_found:
48+
break
49+
50+
if not package_found:
51+
log.warning(f'[exec-code] No Python packages found in {_f}')
4752

4853
setup_code_env(cwd, folders)
4954
return None

src/sphinx_exec_code/sphinx_exec.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,17 @@ def _run(self) -> list:
5757
:return:
5858
"""
5959
output = []
60+
file, line = self.get_source_info()
6061

6162
# format the code
62-
code_show, code_exec = get_show_exec_code(self.content)
63+
try:
64+
code_show, code_exec = get_show_exec_code(self.content)
65+
except Exception as e:
66+
raise ExtensionError(f'Could not parse code markers at {self.get_location()}', orig_exc=e)
6367

6468
# Show the code from the user
6569
create_literal_block(output, code_show, spec=SpecCode.from_options(self.options))
6670

67-
file, line = self.get_source_info()
6871
try:
6972
code_results = execute_code(code_exec, file, line)
7073
except CodeException as e:

tests/test_code_format.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@
44

55

66
def test_format_hide():
7-
code = 'print("1")\n# - hide: start - \nprint("2")\n #hide:stop\nprint("3")'
7+
code = 'print("1")\n# - hide: start - \nprint("2")\n #hide:stop\n \n \nprint("3")'
88
show, run = get_show_exec_code(code.splitlines())
99
assert show == 'print("1")\nprint("3")'
10+
assert run == 'print("1")\nprint("2")\n \n \nprint("3")'
11+
12+
13+
def test_format_skip():
14+
code = 'print("1")\n# - skip: start - \nprint("2")\n #skip:stop\nprint("3")'
15+
show, run = get_show_exec_code(code.splitlines())
16+
assert show == 'print("1")\nprint("2")\nprint("3")'
17+
assert run == 'print("1")\nprint("3")'
1018

1119

1220
def test_marker_err():

0 commit comments

Comments
 (0)