Skip to content

Commit b83e481

Browse files
0.6:
- Fixed an issue where the line numbers for error messages were not correct
1 parent f3d6d76 commit b83e481

File tree

6 files changed

+42
-17
lines changed

6 files changed

+42
-17
lines changed

readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ This code will be executed
3636
```
3737

3838
# Changelog
39+
#### 0.6 (04.04.2022)
40+
- Fixed an issue where the line numbers for error messages were not correct
41+
3942
#### 0.5 (10.03.2022)
4043
- Marked as safe for parallel reading
4144

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.5'
1+
__version__ = '0.6'

src/sphinx_exec_code/code_exec.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def setup_code_env(cwd: Path, folders: Iterable[Path]):
1818
ADDITIONAL_FOLDERS = tuple(map(str, folders))
1919

2020

21-
def execute_code(code: str, file: str, line: int) -> str:
21+
def execute_code(code: str, file: Path, first_loc: int) -> str:
2222
if WORKING_DIR is None or ADDITIONAL_FOLDERS is None:
2323
raise ConfigError('Working dir or additional folders are not set!')
2424

@@ -30,7 +30,7 @@ def execute_code(code: str, file: str, line: int) -> str:
3030

3131
run = subprocess.run([sys.executable, '-c', code], capture_output=True, cwd=WORKING_DIR, env=env)
3232
if run.returncode != 0:
33-
raise CodeException(code, (file, line), run.returncode, run.stderr.decode()) from None
33+
raise CodeException(code, file, first_loc, run.returncode, run.stderr.decode()) from None
3434

3535
ret = (run.stdout.decode() + run.stderr.decode()).strip()
3636

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import re
22
from pathlib import Path
3-
from typing import List, Tuple
3+
from typing import List
44

5-
re_line = re.compile(r'^\s*File "(<string>)", line (\d+), in <module>')
5+
re_line = re.compile(r'^\s*File "(<string>)", line (\d+), in <module>', re.MULTILINE)
66

77

88
class CodeException(Exception):
9-
def __init__(self, code: str, loc: Tuple[str, int], ret: int, stderr: str):
9+
def __init__(self, code: str, file: Path, first_loc: int, ret: int, stderr: str):
1010
self.code = code
11-
self.loc: Tuple[str, int] = loc
11+
12+
self.file: Path = file
13+
self.first_loc: int = first_loc
1214

1315
self.exec_ret = ret
1416
self.exec_err = stderr
@@ -18,19 +20,20 @@ def _err_line(self, lines: List[str]) -> int:
1820
err_line = len(lines)
1921
for m in re_line.finditer(self.exec_err):
2022
err_line = int(m.group(2))
21-
return err_line
23+
return err_line - 1
2224

2325
def pformat(self) -> List[str]:
24-
filename = Path(self.loc[0]).name
26+
filename = self.file.name
2527
code_lines = self.code.splitlines()
2628
err_line = self._err_line(code_lines)
2729

2830
ret = []
2931

3032
# add code snippet
31-
for i in range(max(0, err_line - 8), err_line - 1):
32-
ret.append(f' {code_lines[i]}')
33-
ret.append(f' {code_lines[err_line - 1]} <--')
33+
snip_start = max(0, err_line - 8)
34+
snip_last = min(err_line + 1, len(code_lines))
35+
for i in range(snip_start, snip_last):
36+
ret.append(f' {code_lines[i]}' if i != err_line else f' {code_lines[err_line]} <--')
3437

3538
# add traceback
3639
ret.append('')
@@ -40,6 +43,6 @@ def pformat(self) -> List[str]:
4043
tb_line = tb_line.replace('File "<string>"', f'File "{filename}"')
4144
tb_line = tb_line.replace('File "<string>"', f'File "{filename}"')
4245
tb_line = tb_line.replace(f', line {m.group(2)}, in <module>',
43-
f', line {int(m.group(2)) + self.loc[1] + 1}')
46+
f', line {int(m.group(2)) + self.first_loc - 1}')
4447
ret.append(tb_line)
4548
return ret

src/sphinx_exec_code/sphinx_exec.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import traceback
22
from pathlib import Path
3-
from typing import Optional
3+
from typing import List, Optional
44

55
from docutils import nodes
66
from sphinx.errors import ExtensionError
@@ -61,20 +61,39 @@ def run(self) -> list:
6161
log.error(line)
6262
raise ExtensionError(f'Error while running {name}!', orig_exc=e)
6363

64+
def _get_code_line(self, line_no: int, content: List[str]) -> int:
65+
"""Get the first line number of the code"""
66+
if not content:
67+
return line_no
68+
69+
i = 0
70+
first_line = content[0]
71+
72+
for i, raw_line in enumerate(self.block_text.splitlines()):
73+
# raw line contains the leading white spaces
74+
if raw_line.lstrip() == first_line:
75+
break
76+
77+
return line_no + i
78+
6479
def _run(self) -> list:
6580
""" Executes python code for an RST document, taking input from content or from a filename
6681
:return:
6782
"""
6883
output = []
69-
file, line = self.get_source_info()
84+
raw_file, raw_line = self.get_source_info()
7085
content = self.content
7186

87+
file = Path(raw_file)
88+
line = self._get_code_line(raw_line, content)
89+
7290
code_spec = SpecCode.from_options(self.options)
7391

7492
# Read from example files
7593
if code_spec.filename:
7694
filename = (EXAMPLE_DIR / code_spec.filename).resolve()
7795
content = filename.read_text(encoding='utf-8').splitlines()
96+
file, line = filename, 1
7897

7998
# format the code
8099
try:

tests/test_code_exec.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def test_err(setup_env):
2525
code = "print('Line1')\nprint('Line2')\n1/0"
2626

2727
with pytest.raises(CodeException) as e:
28-
execute_code(code, 'my_file', 5)
28+
execute_code(code, Path('/my_file'), 5)
2929

3030
msg = e.value.pformat()
3131
assert msg == [
@@ -34,6 +34,6 @@ def test_err(setup_env):
3434
' 1/0 <--',
3535
'',
3636
'Traceback (most recent call last):',
37-
' File "my_file", line 9',
37+
' File "my_file", line 7',
3838
'ZeroDivisionError: division by zero'
3939
]

0 commit comments

Comments
 (0)