Skip to content

Commit

Permalink
Merge pull request #113 from Erotemic/dev/0.15.10
Browse files Browse the repository at this point in the history
Dev/0.15.10
  • Loading branch information
Erotemic authored Oct 6, 2021
2 parents 16b4efc + 9e909ef commit b959028
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name: Tests
on:
push:
pull_request:
branches: [ master ]
branches: [ main ]

jobs:

Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ We are currently working on porting this changelog to the specifications in
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Version 0.15.10 - Unreleased


### Changed

* The xdoctest "analysis" option now defaults to "auto" everywhere.

### Fixed

* Fix issue #112 `--analysis=dynamic` argument is now respected


## Version 0.15.9 - Released 2021-09-24

### Changed
Expand Down
14 changes: 8 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
|CircleCI| |Travis| |Appveyor| |Codecov| |Pypi| |Downloads| |ReadTheDocs|
|GithubActions| |CircleCI| |Appveyor| |Codecov| |Pypi| |Downloads| |ReadTheDocs|


.. The large version wont work because github strips rst image rescaling.
Expand Down Expand Up @@ -399,15 +399,17 @@ doctests are getting too long.

.. |CircleCI| image:: https://circleci.com/gh/Erotemic/xdoctest.svg?style=svg
:target: https://circleci.com/gh/Erotemic/xdoctest
.. |Travis| image:: https://img.shields.io/travis/Erotemic/xdoctest/master.svg?label=Travis%20CI
.. |Travis| image:: https://img.shields.io/travis/Erotemic/xdoctest/main.svg?label=Travis%20CI
:target: https://travis-ci.org/Erotemic/xdoctest
.. |Appveyor| image:: https://ci.appveyor.com/api/projects/status/github/Erotemic/xdoctest?branch=master&svg=True
:target: https://ci.appveyor.com/project/Erotemic/xdoctest/branch/master
.. |Codecov| image:: https://codecov.io/github/Erotemic/xdoctest/badge.svg?branch=master&service=github
:target: https://codecov.io/github/Erotemic/xdoctest?branch=master
.. |Appveyor| image:: https://ci.appveyor.com/api/projects/status/github/Erotemic/xdoctest?branch=main&svg=True
:target: https://ci.appveyor.com/project/Erotemic/xdoctest/branch/main
.. |Codecov| image:: https://codecov.io/github/Erotemic/xdoctest/badge.svg?branch=main&service=github
:target: https://codecov.io/github/Erotemic/xdoctest?branch=main
.. |Pypi| image:: https://img.shields.io/pypi/v/xdoctest.svg
:target: https://pypi.python.org/pypi/xdoctest
.. |Downloads| image:: https://img.shields.io/pypi/dm/xdoctest.svg
:target: https://pypistats.org/packages/xdoctest
.. |ReadTheDocs| image:: https://readthedocs.org/projects/xdoctest/badge/?version=latest
:target: https://xdoctest.readthedocs.io
.. |GithubActions| image:: https://github.com/Erotemic/xdoctest/actions/workflows/tests.yml/badge.svg?branch=main
:target: https://github.com/Erotemic/xdoctest/actions?query=branch%3Amain
18 changes: 18 additions & 0 deletions dev/demo_dynamic_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
CommandLine:
xdoctest ~/code/xdoctest/dev/demo_dynamic_analysis.py --analysis=auto
xdoctest ~/code/xdoctest/dev/demo_dynamic_analysis.py --analysis=dynamic
xdoctest ~/code/xdoctest/dev/demo_dynamic_analysis.py --xdoc-force-dynamic
"""


def func() -> None:
r''' Dynamic doctest
>>> %s
%s
'''
return

func.__doc__ %= ('print(1)', '1')
66 changes: 66 additions & 0 deletions dev/demo_usage_with_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""
demo_usage_with_logger.py
Script to demo a workaround to [Issue111]_.
CommandLine:
# Run with xdoctest runner
xdoctest ~/code/xdoctest/dev/demo_usage_with_logger.py
# Run with pytest runner
pytest -s --xdoctest --xdoctest-verbose=3 ~/code/xdoctest/dev/demo_usage_with_logger.py
# Run with builtin main
python ~/code/xdoctest/dev/demo_usage_with_logger.py
References:
.. [Issue111] https://github.com/Erotemic/xdoctest/issues/111
"""
import logging
import sys


class StreamHandler2(logging.StreamHandler):
def __init__(self, _getstream=None):
"""
Initialize the handler.
If stream is not specified, sys.stderr is used.
"""
logging.Handler.__init__(self)
if _getstream is None:
_getstream = lambda: sys.stderr # NOQA
self._getstream = _getstream
self.__class__.stream = property(lambda self: self._getstream())

def setStream(self, stream):
raise NotImplementedError


handler = StreamHandler2(lambda: sys.stdout)

_log = logging.getLogger('mylog')
_log.setLevel(logging.INFO)
_log.addHandler(handler)

_log.info('hello')
_log.info('hello hello')


def func_with_doctest():
"""
Example:
>>> _log.info('greetings from my doctest')
greetings from my doctest
"""


def main():
import xdoctest
xdoctest.doctest_callable(func_with_doctest)


if __name__ == '__main__':
main()
47 changes: 47 additions & 0 deletions testing/test_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,53 @@ def test_defined_by_module():
assert not flag, '{} should be not defined by {}'.format(item, module)


def test_programatically_generated_docstrings():
"""
Test that the "dynamic" analysis mode works on dynamically generated
docstrings.
"""
from xdoctest import utils
from xdoctest.utils.util_misc import TempModule
temp = TempModule(utils.codeblock(
'''
code = ">>> print('hello world')"
def func1():
"""
Example:
{}
"""
func1.__doc__ = func1.__doc__.format(code)
'''))

import xdoctest
# auto wont pick up dynamic doctests by default
# Although in the future it would be cool if it did
result = xdoctest.doctest_module(temp.modpath, analysis='auto', command='all')
assert result['n_total'] == 1
assert result['n_failed'] == 0

# but an explicit dynamic should pick these up
result = xdoctest.doctest_module(temp.modpath, analysis='dynamic', command='all')
assert result['n_passed'] == 1
assert result['n_failed'] == 0

# module = ub.import_module_from_path(temp.modpath)
# assert module.func1.__doc__ is not None
# list(xdoctest.core.parse_doctestables(temp.modpath, analysis='dynamic'))
# xdoctest.core.DEBUG = 1
# calldefs = list(xdoctest.core.package_calldefs(temp.modpath, analysis='dynamic'))
# calldefs = list(xdoctest.core.parse_calldefs(temp.modpath, analysis='dynamic'))
# from xdoctest import dynamic_analysis
# from xdoctest import static_analysis
# static_analysis.parse_static_calldefs(fpath=temp.modpath)
# node = dynamic_analysis.parse_dynamic_calldefs(temp.modpath)['func1']
# node = dynamic_analysis.parse_dynamic_calldefs(temp.modpath)['func2']


if __name__ == '__main__':
"""
CommandLine:
Expand Down
2 changes: 1 addition & 1 deletion xdoctest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def fib(n):
mkinit xdoctest --nomods
'''

__version__ = '0.15.9'
__version__ = '0.15.10'


# Expose only select submodules
Expand Down
12 changes: 9 additions & 3 deletions xdoctest/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,9 @@ def package_calldefs(pkg_identifier, exclude=[], ignore_syntax_errors=True,
>>> assert util_import.modpath_to_modname(modpath) == pkg_identifier
>>> assert 'package_calldefs' in calldefs
"""
if DEBUG:
print('Find package calldefs: pkg_identifier = {!r}'.format(pkg_identifier))

if isinstance(pkg_identifier, types.ModuleType):
# Case where we are forced to use a live module
identifiers = [pkg_identifier]
Expand All @@ -494,7 +497,7 @@ def package_calldefs(pkg_identifier, exclude=[], ignore_syntax_errors=True,
'Is it an old pyc file?'.format(modname))
continue
try:
calldefs = parse_calldefs(module_identifier)
calldefs = parse_calldefs(module_identifier, analysis=analysis)
if calldefs is not None:
yield calldefs, module_identifier
except SyntaxError as ex:
Expand Down Expand Up @@ -564,6 +567,9 @@ def parse_calldefs(module_identifier, analysis='auto'):
else:
raise KeyError(analysis)

if DEBUG:
print('About to parse calldefs with do_dynamic={}'.format(do_dynamic))

calldefs = None
if do_dynamic:
try:
Expand All @@ -586,7 +592,7 @@ def parse_calldefs(module_identifier, analysis='auto'):

def parse_doctestables(module_identifier, exclude=[], style='auto',
ignore_syntax_errors=True, parser_kw={},
analysis='static'):
analysis='auto'):
"""
Parses all doctests within top-level callables of a module and generates
example objects. The style influences which tests are found.
Expand All @@ -604,7 +610,7 @@ def parse_doctestables(module_identifier, exclude=[], style='auto',
parser_kw: extra args passed to the parser
analysis (str, default='static'):
analysis (str, default='auto'):
if 'static', only static analysis is used to parse call
definitions. If 'auto', uses dynamic analysis for compiled python
extensions, but static analysis elsewhere, if 'dynamic', then
Expand Down
4 changes: 2 additions & 2 deletions xdoctest/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def doctest_callable(func):

def doctest_module(module_identifier=None, command=None, argv=None, exclude=[],
style='auto', verbose=None, config=None, durations=None,
analysis='static'):
analysis='auto'):
"""
Executes requestsed google-style doctests in a package or module.
Main entry point into the testing framework.
Expand Down Expand Up @@ -585,7 +585,7 @@ def str_lower(x):

add_argument(*('--analysis',), type=str,
help='How doctests are collected',
choices=['auto', 'static', 'dynamic'], default='static')
choices=['auto', 'static', 'dynamic'], default='auto')

add_argument(*('--durations',), type=int,
help=('Specify execution times for slowest N tests.'
Expand Down

0 comments on commit b959028

Please sign in to comment.