Skip to content

Commit

Permalink
Merge pull request #132 from Erotemic/dev/1.0.2
Browse files Browse the repository at this point in the history
Defer pre-imports until a non-skipped line is about to
  • Loading branch information
Erotemic authored Aug 19, 2022
2 parents 79ea48a + d243b7f commit 38a82eb
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 27 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
skip.
* Disabled traceback suppression on module import errors (this is is
configurable via the "supress_import_errors" option).

* Xdoctest will no longer try to pre-import the module if none of its doctests
have any enabled lines. This also means global-exec statements will NOT run
for those tests, which means you can no longer use global-exec to
force enabling tests.


## Version 1.0.1 - Released 2022-07-10
Expand Down
55 changes: 32 additions & 23 deletions src/xdoctest/doctest_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,29 +677,9 @@ def run(self, verbose=None, on_error=None):
# setup reporting choice
runstate.set_report_style(self.config['reportchoice'].lower())

try:
self._import_module()
except Exception:
self.failed_part = '<IMPORT>'
self._partfilename = '<doctest:' + self.node + ':pre_import>'
self.exc_info = sys.exc_info()
if on_error == 'raise':
raise
else:
summary = self._post_run(verbose)
return summary

test_globals, compileflags = self._test_globals()
global_exec = self.config.getvalue('global_exec')
if global_exec:
# Hack to make it easier to specify multi-line input on the CLI
global_source = utils.codeblock(global_exec.replace('\\n', '\n'))
global_code = compile(
global_source, mode='exec',
filename='<doctest:' + self.node + ':' + 'global_exec>',
flags=compileflags, dont_inherit=True
)
exec(global_code, test_globals)
# Defer the execution of the pre-import until we know at least one part
# in the doctest will run.
did_pre_import = False

# Can't do this because we can't force execution of SCRIPTS
# if self.is_disabled():
Expand Down Expand Up @@ -744,6 +724,35 @@ def run(self, verbose=None, on_error=None):
self._skipped_parts.append(part)
continue

if not did_pre_import:
# Execute the pre-import before the first run of
# non-skipped code.
try:
self._import_module()
except Exception:
self.failed_part = '<IMPORT>'
self._partfilename = '<doctest:' + self.node + ':pre_import>'
self.exc_info = sys.exc_info()
if on_error == 'raise':
raise
else:
summary = self._post_run(verbose)
return summary

test_globals, compileflags = self._test_globals()
global_exec = self.config.getvalue('global_exec')
if global_exec:
# Hack to make it easier to specify multi-line input on the CLI
global_source = utils.codeblock(global_exec.replace('\\n', '\n'))
global_code = compile(
global_source, mode='exec',
filename='<doctest:' + self.node + ':' + 'global_exec>',
flags=compileflags, dont_inherit=True
)
exec(global_code, test_globals)

did_pre_import = True

try:
# Compile code, handle syntax errors
# part.compile_mode can be single, exec, or eval.
Expand Down
43 changes: 43 additions & 0 deletions tests/test_preimport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from os.path import join
from xdoctest import utils


def test_preimport_skiped_on_disabled_module():
"""
If our module has no enabled tests, pre-import should never run.
"""

from xdoctest import runner
import os

source = utils.codeblock(
'''
raise Exception("DONT IMPORT ME!")
def ima_function():
"""
Example:
>>> # xdoctest: +REQUIRES(env:XDOCTEST_TEST_DOITANYWAY)
>>> print('hello')
"""
''')

with utils.TempDir() as temp:
dpath = temp.dpath
modpath = join(dpath, 'test_bad_preimport.py')
with open(modpath, 'w') as file:
file.write(source)
os.environ['XDOCTEST_TEST_DOITANYWAY'] = ''
with utils.CaptureStdout() as cap:
runner.doctest_module(modpath, 'all', argv=[''])
assert 'Failed to import modname' not in cap.text
assert '1 skipped' in cap.text

os.environ['XDOCTEST_TEST_DOITANYWAY'] = '1'
with utils.CaptureStdout() as cap:
runner.doctest_module(modpath, 'all', argv=[])
assert 'Failed to import modname' in cap.text

del os.environ['XDOCTEST_TEST_DOITANYWAY']
9 changes: 6 additions & 3 deletions tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,9 @@ def test_hack_the_sys_argv():
"""
Tests hacky solution to issue #76
pytest tests/test_runner.py::test_global_exec -s
NOTE: in version 1.0.2 this hack no longer works!
pytest tests/test_runner.py::test_hack_the_sys_argv -s
References:
https://github.com/Erotemic/xdoctest/issues/76
Expand Down Expand Up @@ -461,12 +463,13 @@ def foo():
with utils.CaptureStdout() as cap:
runner.doctest_module(modpath, 'foo', argv=[''], config=config)

if NEEDS_FIX:
if 0 and NEEDS_FIX:
# Fix the global state
sys.argv.remove('--hackedflag')

# print(cap.text)
assert '1 passed' in cap.text
assert '1 skipped' in cap.text
# assert '1 passed' in cap.text


if __name__ == '__main__':
Expand Down

0 comments on commit 38a82eb

Please sign in to comment.