Skip to content

Commit

Permalink
0.3
Browse files Browse the repository at this point in the history
- Added some more documentation and fixed some false path warnings
  • Loading branch information
spacemanspiff2007 committed Sep 24, 2021
1 parent b08b984 commit 4c90c2b
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 44 deletions.
69 changes: 60 additions & 9 deletions doc/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@ Usage

Without options
------------------------------
.. code-block:: rst
.. code-block:: python
.. exec_code::
print('Easy!')
Generated view:
Generated view

----

.. exec_code::

print('Easy!')

----

Options
------------------------------
It's possible to further configure both the code block and the output block with the following options:
Expand All @@ -34,7 +38,7 @@ language/language_output:

Example:

.. code-block:: rst
.. code-block:: python
.. exec_code::
:linenos:
Expand All @@ -43,7 +47,9 @@ Example:
print('Easy!')
Generated view:
Generated view

----

.. exec_code::
:linenos:
Expand All @@ -52,12 +58,15 @@ Generated view:

print('Easy!')

----


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

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

Expand All @@ -69,33 +78,75 @@ Spaces and dashes are ignored for the case insensitive marker detection so these
# ----- hide: start -----
Example:

.. code-block:: rst
Hiding code parts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
.. exec_code::
# --- hide: start ---
print('Setup!')
#hide:toggle
print('Easy!')
# --- hide: start ---
print('Hidden!')
# --- hide: stop ---
# Note the missing entries!
print('Visible!')
Generated view:
Generated view (note the skipped empty lines after the stop and disabling toggle marker)

----

.. exec_code::

# --- hide: start ---
print('Setup!')
#hide:toggle

print('Easy!')

# --- hide: start ---
print('Hidden!')
# --- hide: stop ---

# Note the missing entries!
print('Visible!')

----

Skipping code parts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
.. exec_code::
# --- skip: start ---
print(f'1 / 0 = {1 / 0}')
# --- skip: stop ---
# --- hide: start ---
print('1 / 0 = 0')
# --- hide: stop ---
Generated view

----

.. exec_code::

# --- skip: start ---
print(f'1 / 0 = {1 / 0}')
# --- skip: stop ---

# --- hide: start ---
print('1 / 0 = 0')
# --- hide: stop ---

----

With the combination of ``skip`` and ``hide`` it's possible to "simulate" every code.
16 changes: 13 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Sphinx-exec-code allows execution of any python code during the documentation bu
It's also possible to display the output of the code execution.

With this extension it's easy to ensure that the provided code samples are always working.
Additionally, it's possible to inspect and print output of the documented code.
Additionally, it's possible to show the output of the documented code.

Each code snippet runs in a fresh interpreter so changes to not leak between executions.

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

````text
.. exec-code::
:hide_output:
print('This code will be executed')
````

generates
```python
print('This code will be executed')
```
```
This code will be executed
```

# Changelog
#### 0.3 (24.09.2021)
- Added some more documentation and fixed some false path warnings

#### 0.2 (21.09.2021)
- Initial Release
2 changes: 1 addition & 1 deletion src/sphinx_exec_code/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.2'
__version__ = '0.3'
82 changes: 57 additions & 25 deletions src/sphinx_exec_code/code_format.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,73 @@
from typing import Iterable, List, Tuple
from typing import Iterable, Tuple


class VisibilityMarkerError(Exception):
pass


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

marker_start = f'#{marker}:start'
marker_stop = f'#{marker}:stop'
marker_toggle = f'#{marker}:toggle'
def __init__(self, marker: str):
assert marker in CodeMarker.MARKERS
self.start = f'#{marker}:start'
self.stop = f'#{marker}:stop'
self.toggle = f'#{marker}:toggle'

self.do_add = True
self.skip_empty = False
self.lines = []

def is_marker(self, line: str) -> bool:
if line == self.start:
if not self.do_add:
raise VisibilityMarkerError(f'{self.start[1:]} already called! '
f'Use {self.stop[1:]} or {self.toggle[1:]}!')
self.do_add = False
return True

if line == self.stop:
if self.do_add:
raise VisibilityMarkerError(f'{self.stop[1:]} already called! '
f'Use {self.start[1:]} or {self.toggle[1:]}!')
self.do_add = True
self.skip_empty = True
return True

if line == self.toggle:
self.do_add = not self.do_add
return True

return False

def add_line(self, line: str):
if not self.do_add:
return None

if self.skip_empty:
if not line.strip():
return None
self.skip_empty = False

self.lines.append(line)


def get_show_exec_code(code_lines: Iterable[str]) -> Tuple[str, str]:
hide = CodeMarker('hide')
skip = CodeMarker('skip')

lines = []
do_add = True
for org_line in code_lines:
line = org_line.replace(' ', '').replace('-', '').lower()
if line == marker_start:
if not do_add:
raise VisibilityMarkerError(f'{marker}:start already called! Use {marker}:stop or {marker}:toggle!')
do_add = False
continue
elif line == marker_stop:
if do_add:
raise VisibilityMarkerError(f'{marker}:stop already called! Use {marker}:start or {marker}:toggle!')
do_add = True

if hide.is_marker(line):
continue
elif line == marker_toggle:
do_add = not do_add
if skip.is_marker(line):
continue

if do_add:
lines.append(org_line)
return lines

hide.add_line(org_line)
skip.add_line(org_line)

def get_show_exec_code(code_lines: Iterable[str]) -> Tuple[str, str]:
shown_code = '\n'.join(_process_code(code_lines, 'hide'))
executed_code = '\n'.join(_process_code(code_lines, 'skip'))
shown_code = '\n'.join(hide.lines)
executed_code = '\n'.join(skip.lines)

return shown_code.strip(), executed_code.strip()
11 changes: 8 additions & 3 deletions src/sphinx_exec_code/sphinx_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,23 @@ def builder_ready(app):
raise FileNotFoundError(f'Additional directory "{_f}" not found! (configured by {CONF_NAME_DIRS})')

# Search for a python package and print a warning if we find none
# since this is the only reason to specify a working dir
# since this is the only reason to specify additional folders
for _f in folders:
package_found = False
for __f in _f.iterdir():
if not __f.is_dir():
continue

# log warning if we don't find a python package
for file in __f.iterdir():
if file.name == '__init__.py':
package_found = True
break
else:
log.warning(f'[exec-code] No Python package found in {_f}')
if package_found:
break

if not package_found:
log.warning(f'[exec-code] No Python packages found in {_f}')

setup_code_env(cwd, folders)
return None
Expand Down
7 changes: 5 additions & 2 deletions src/sphinx_exec_code/sphinx_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,17 @@ def _run(self) -> list:
:return:
"""
output = []
file, line = self.get_source_info()

# format the code
code_show, code_exec = get_show_exec_code(self.content)
try:
code_show, code_exec = get_show_exec_code(self.content)
except Exception as e:
raise ExtensionError(f'Could not parse code markers at {self.get_location()}', orig_exc=e)

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

file, line = self.get_source_info()
try:
code_results = execute_code(code_exec, file, line)
except CodeException as e:
Expand Down
10 changes: 9 additions & 1 deletion tests/test_code_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@


def test_format_hide():
code = 'print("1")\n# - hide: start - \nprint("2")\n #hide:stop\nprint("3")'
code = 'print("1")\n# - hide: start - \nprint("2")\n #hide:stop\n \n \nprint("3")'
show, run = get_show_exec_code(code.splitlines())
assert show == 'print("1")\nprint("3")'
assert run == 'print("1")\nprint("2")\n \n \nprint("3")'


def test_format_skip():
code = 'print("1")\n# - skip: start - \nprint("2")\n #skip:stop\nprint("3")'
show, run = get_show_exec_code(code.splitlines())
assert show == 'print("1")\nprint("2")\nprint("3")'
assert run == 'print("1")\nprint("3")'


def test_marker_err():
Expand Down

0 comments on commit 4c90c2b

Please sign in to comment.