def test_warning_location(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.warning('message1', location='index') assert 'index.txt: WARNING: message1' in warning.getvalue() logger.warning('message2', location=('index', 10)) assert 'index.txt:10: WARNING: message2' in warning.getvalue() logger.warning('message3', location=None) assert colorize('darkred', 'WARNING: message3') in warning.getvalue() node = nodes.Node() node.source, node.line = ('index.txt', 10) logger.warning('message4', location=node) assert 'index.txt:10: WARNING: message4' in warning.getvalue() node.source, node.line = ('index.txt', None) logger.warning('message5', location=node) assert 'index.txt:: WARNING: message5' in warning.getvalue() node.source, node.line = (None, 10) logger.warning('message6', location=node) assert '<unknown>:10: WARNING: message6' in warning.getvalue() node.source, node.line = (None, None) logger.warning('message7', location=node) assert colorize('darkred', 'WARNING: message7') in warning.getvalue()
def test_add_is_parallel_allowed(app, status, warning): logging.setup(app, status, warning) assert app.is_parallel_allowed('read') is True assert app.is_parallel_allowed('write') is True assert warning.getvalue() == '' app.setup_extension('read_parallel') assert app.is_parallel_allowed('read') is True assert app.is_parallel_allowed('write') is True assert warning.getvalue() == '' app.extensions.pop('read_parallel') app.setup_extension('write_parallel') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is True assert ("the write_parallel extension does not declare if it is safe " "for parallel reading, assuming it isn't - please ") in warning.getvalue() app.extensions.pop('write_parallel') warning.truncate(0) # reset warnings app.setup_extension('read_serial') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is True assert warning.getvalue() == '' app.extensions.pop('read_serial') app.setup_extension('write_serial') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is False assert ("the write_serial extension does not declare if it is safe " "for parallel reading, assuming it isn't - please ") in warning.getvalue() app.extensions.pop('write_serial') warning.truncate(0) # reset warnings
def test_suppress_warnings(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) app._warncount = 0 # force reset app.config.suppress_warnings = [] warning.truncate(0) logger.warning('message1', type='test', subtype='logging') logger.warning('message2', type='test', subtype='crash') logger.warning('message3', type='actual', subtype='logging') assert 'message1' in warning.getvalue() assert 'message2' in warning.getvalue() assert 'message3' in warning.getvalue() assert app._warncount == 3 app.config.suppress_warnings = ['test'] warning.truncate(0) logger.warning('message1', type='test', subtype='logging') logger.warning('message2', type='test', subtype='crash') logger.warning('message3', type='actual', subtype='logging') assert 'message1' not in warning.getvalue() assert 'message2' not in warning.getvalue() assert 'message3' in warning.getvalue() assert app._warncount == 4 app.config.suppress_warnings = ['test.logging'] warning.truncate(0) logger.warning('message1', type='test', subtype='logging') logger.warning('message2', type='test', subtype='crash') logger.warning('message3', type='actual', subtype='logging') assert 'message1' not in warning.getvalue() assert 'message2' in warning.getvalue() assert 'message3' in warning.getvalue() assert app._warncount == 6
def test_colored_logs(app, status, warning): app.verbosity = 2 logging.setup(app, status, warning) logger = logging.getLogger(__name__) # default colors logger.debug('message1') logger.verbose('message2') logger.info('message3') logger.warning('message4') logger.critical('message5') logger.error('message6') assert colorize('darkgray', 'message1') in status.getvalue() assert 'message2\n' in status.getvalue() # not colored assert 'message3\n' in status.getvalue() # not colored assert colorize('darkred', 'WARNING: message4') in warning.getvalue() assert 'WARNING: message5\n' in warning.getvalue() # not colored assert 'WARNING: message6\n' in warning.getvalue() # not colored # color specification logger.debug('message7', color='white') logger.info('message8', color='red') assert colorize('white', 'message7') in status.getvalue() assert colorize('red', 'message8') in status.getvalue()
def test_new_documenter(): logging.setup(app, app._status, app._warning) class MyDocumenter(ModuleLevelDocumenter): objtype = 'integer' directivetype = 'data' priority = 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, int) def document_members(self, all_members=False): return add_documenter(MyDocumenter) def assert_result_contains(item, objtype, name, **kw): app._warning.truncate(0) inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) # print '\n'.join(directive.result) assert app._warning.getvalue() == '' assert item in directive.result del directive.result[:] options.members = ['integer'] assert_result_contains('.. py:data:: integer', 'module', 'target')
def test_info_location(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.info('message1', location='index') assert 'index.txt: message1' in status.getvalue() logger.info('message2', location=('index', 10)) assert 'index.txt:10: message2' in status.getvalue() logger.info('message3', location=None) assert '\nmessage3' in status.getvalue() node = nodes.Node() node.source, node.line = ('index.txt', 10) logger.info('message4', location=node) assert 'index.txt:10: message4' in status.getvalue() node.source, node.line = ('index.txt', None) logger.info('message5', location=node) assert 'index.txt:: message5' in status.getvalue() node.source, node.line = (None, 10) logger.info('message6', location=node) assert '<unknown>:10: message6' in status.getvalue() node.source, node.line = (None, None) logger.info('message7', location=node) assert '\nmessage7' in status.getvalue()
def test_status_iterator(app, status, warning): logging.setup(app, status, warning) # test for old_status_iterator status.truncate(0) yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ')) output = strip_escseq(status.getvalue()) assert 'testing ... hello sphinx world \n' in output assert yields == ['hello', 'sphinx', 'world'] # test for status_iterator (verbosity=0) status.truncate(0) yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ', length=3, verbosity=0)) output = strip_escseq(status.getvalue()) assert 'testing ... [ 33%] hello \r' in output assert 'testing ... [ 66%] sphinx \r' in output assert 'testing ... [100%] world \r\n' in output assert yields == ['hello', 'sphinx', 'world'] # test for status_iterator (verbosity=1) status.truncate(0) yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ', length=3, verbosity=1)) output = strip_escseq(status.getvalue()) assert 'testing ... [ 33%] hello\n' in output assert 'testing ... [ 66%] sphinx\n' in output assert 'testing ... [100%] world\n\n' in output assert yields == ['hello', 'sphinx', 'world']
def test_nonl_info_log(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.info('message1', nonl=True) logger.info('message2') logger.info('message3') assert 'message1message2\nmessage3' in status.getvalue()
def __init__(self, confoverrides=None, srcdir=None, raise_on_warning=False): self.extensions = {} self.registry = SphinxComponentRegistry() try: self.html_themes = {} except AttributeError: # changed to property in sphinx 4.1 pass self.events = EventManager(self) # logging self.verbosity = 0 self._warncount = 0 self.warningiserror = raise_on_warning self._status = StringIO() self._warning = StringIO() logging.setup(self, self._status, self._warning) self.tags = Tags([]) self.config = Config({}, confoverrides or {}) self.config.pre_init_values() self._init_i18n() for extension in builtin_extensions: self.registry.load_extension(self, extension) # fresh env self.doctreedir = "" self.srcdir = srcdir self.confdir = None self.outdir = "" self.project = Project(srcdir=srcdir, source_suffix={".md": "markdown"}) self.project.docnames = {"mock_docname"} self.env = BuildEnvironment() self.env.setup(self) self.env.temp_data["docname"] = "mock_docname" # Ignore type checkers because we disrespect superclass typing here self.builder = None # type: ignore[assignment] if not with_builder: return # this code is only required for more complex parsing with extensions for extension in self.config.extensions: self.setup_extension(extension) buildername = "dummy" self.preload_builder(buildername) self.config.init_values() self.events.emit("config-inited", self.config) with tempfile.TemporaryDirectory() as tempdir: # creating a builder attempts to make the doctreedir self.doctreedir = tempdir self.builder = self.create_builder(buildername) self.doctreedir = ""
def test_once_warning_log(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.warning('message: %d', 1, once=True) logger.warning('message: %d', 1, once=True) logger.warning('message: %d', 2, once=True) assert 'WARNING: message: 1\nWARNING: message: 2\n' in strip_escseq(warning.getvalue())
def test_annotation_html(app, status, warning): app.config.sphinx_autodoc_alias = {("A", "B"), ("C", "D")} app.verbosity = 2 logging.setup(app, status, warning) app.builder.doctreedir.rmtree() app.builder.outdir.rmtree() app.builder.build_all() assert not warning.getvalue() messages = [i for i in status.getvalue().split("\n") if "[autodoc-typehints][process-docstring] " in i] assert len(messages) == 5
def test_suppress_logging(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.warning('message1') with logging.suppress_logging(): logger.warning('message2') assert 'WARNING: message1' in warning.getvalue() assert 'WARNING: message2' not in warning.getvalue() assert 'WARNING: message1' in warning.getvalue() assert 'WARNING: message2' not in warning.getvalue()
def test_warningiserror(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) # if False, warning is not error app.warningiserror = False logger.warning('message') # if True, warning raises SphinxWarning exception app.warningiserror = True with pytest.raises(SphinxWarning): logger.warning('message')
def test_output_with_unencodable_char(app, status, warning): class StreamWriter(codecs.StreamWriter): def write(self, object): self.stream.write(object.encode('cp1252').decode('cp1252')) logging.setup(app, StreamWriter(status), warning) logger = logging.getLogger(__name__) # info with UnicodeEncodeError status.truncate(0) status.seek(0) logger.info(u"unicode \u206d...") assert status.getvalue() == "unicode ?...\n"
def main(argv: List[str] = sys.argv[1:]) -> None: sphinx.locale.setlocale(locale.LC_ALL, '') sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx') app = DummyApplication() logging.setup(app, sys.stdout, sys.stderr) # type: ignore setup_documenters(app) args = get_parser().parse_args(argv) generate_autosummary_docs(args.source_file, args.output_dir, '.' + args.suffix, template_dir=args.templates, imported_members=args.imported_members, app=app)
def test_logging_in_ParallelTasks(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) def child_process(): logger.info('message1') logger.warning('message2', location='index') tasks = ParallelTasks(1) tasks.add_task(child_process) tasks.join() assert 'message1' in status.getvalue() assert 'index.txt: WARNING: message2' in warning.getvalue()
def test_execfile_python2(capsys, app, status, warning, tempdir): logging.setup(app, status, warning) conf_py = tempdir / 'conf.py' conf_py.write_bytes(b'print "hello"\n') execfile_(conf_py, {}) msg = ('Support for evaluating Python 2 syntax is deprecated ' 'and will be removed in Sphinx 4.0. ' 'Convert %s to Python 3 syntax.\n' % conf_py) assert msg in strip_escseq(warning.getvalue()) captured = capsys.readouterr() assert captured.out == 'hello\n'
def __init__(self, confoverrides=None, srcdir=None, raise_on_warning=False): self.extensions = {} self.registry = SphinxComponentRegistry() self.html_themes = {} self.events = EventManager(self) # logging self.verbosity = 0 self._warncount = 0 self.warningiserror = raise_on_warning self._status = StringIO() self._warning = StringIO() logging.setup(self, self._status, self._warning) self.tags = Tags(None) self.config = Config({}, confoverrides or {}) self.config.pre_init_values() self._init_i18n() for extension in builtin_extensions: self.registry.load_extension(self, extension) # fresh env self.doctreedir = None self.srcdir = srcdir self.confdir = None self.outdir = None self.project = Project(srcdir=srcdir, source_suffix=".md") self.project.docnames = ["mock_docname"] self.env = BuildEnvironment() self.env.setup(self) self.env.temp_data["docname"] = "mock_docname" self.builder = None if not with_builder: return # this code is only required for more complex parsing with extensions for extension in self.config.extensions: self.setup_extension(extension) buildername = "dummy" self.preload_builder(buildername) self.config.init_values() self.events.emit("config-inited", self.config) with tempfile.TemporaryDirectory() as tempdir: # creating a builder attempts to make the doctreedir self.doctreedir = tempdir self.builder = self.create_builder(buildername) self.doctreedir = None
def test_execfile_python2(capsys, app, status, warning): logging.setup(app, status, warning) ns = {} with tempfile.NamedTemporaryFile() as tmp: tmp.write(b'print "hello"\n') tmp.flush() execfile_(tmp.name, ns) msg = ('Support for evaluating Python 2 syntax is deprecated ' 'and will be removed in Sphinx 4.0. ' 'Convert %s to Python 3 syntax.\n' % tmp.name) assert msg in strip_escseq(warning.getvalue()) captured = capsys.readouterr() assert captured.out == 'hello\n'
def test_advanced(app, status, warning): logging.setup(app, status, warning) app.builder.build_all() assert (app.outdir / 'api').isdir() assert (app.outdir / 'api' / 'modules.html').exists() for module in [ 'apidoc_dummy_module.html', 'apidoc_dummy_package.apidoc_dummy_submodule_a.html', 'apidoc_dummy_package.apidoc_dummy_submodule_b.html' ]: assert (app.outdir / 'api' / module).exists() assert (app.outdir / 'api' / 'apidoc_dummy_package.html').exists() assert not (app.outdir / 'api' / 'conf.html').exists() assert not warning.getvalue()
def sphinx_mute(self): try: original_status = self.sphinx._status original_warning = self.sphinx._warning self.sphinx._status = StringIO() self.sphinx._warning = StringIO() logging.setup(self.sphinx, self.sphinx._status, self.sphinx._warning) yield finally: self.sphinx._status = original_status self.sphinx._warning = original_warning logging.setup(self.sphinx, self.sphinx._status, self.sphinx._warning)
def test_pending_warnings(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.warning('message1') with logging.pending_warnings(): # not logged yet (bufferred) in here logger.warning('message2') logger.warning('message3') assert 'WARNING: message1' in warning.getvalue() assert 'WARNING: message2' not in warning.getvalue() assert 'WARNING: message3' not in warning.getvalue() # actually logged as ordered assert 'WARNING: message2\nWARNING: message3' in strip_escseq(warning.getvalue())
def test_basics(app, status, warning): logging.setup(app, status, warning) app.builder.build_all() assert (app.srcdir / 'api').isdir() assert (app.srcdir / 'api' / 'modules.rst').exists() assert (app.srcdir / 'api' / 'apidoc_dummy_module.rst').exists() assert not (app.srcdir / 'api' / 'conf.rst').exists() assert (app.outdir / 'api').isdir() assert (app.outdir / 'api' / 'modules.html').exists() assert (app.outdir / 'api' / 'apidoc_dummy_module.html').exists() assert not (app.outdir / 'api' / 'conf.html').exists() assert not warning.getvalue()
def test_execfile_python2(capsys, app, status, warning): logging.setup(app, status, warning) ns = {} with tempfile.NamedTemporaryFile() as tmp: tmp.write(b'print "hello"\n') tmp.flush() execfile_(tmp.name, ns) msg = ( 'Support for evaluating Python 2 syntax is deprecated ' 'and will be removed in Sphinx 4.0. ' 'Convert %s to Python 3 syntax.\n' % tmp.name) assert msg in strip_escseq(warning.getvalue()) captured = capsys.readouterr() assert captured.out == 'hello\n'
def test_warningiserror(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) # if False, warning is not error app.warningiserror = False logger.warning('message') # if True, warning raises SphinxWarning exception app.warningiserror = True with pytest.raises(SphinxWarning): logger.warning('message: %s', 'arg') # message contains format string (refs: #4070) with pytest.raises(SphinxWarning): logger.warning('%s')
def test_prefixed_warnings(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.warning('message1') with prefixed_warnings('PREFIX:'): logger.warning('message2') with prefixed_warnings('Another PREFIX:'): logger.warning('message3') logger.warning('message4') logger.warning('message5') assert 'WARNING: message1' in warning.getvalue() assert 'WARNING: PREFIX: message2' in warning.getvalue() assert 'WARNING: Another PREFIX: message3' in warning.getvalue() assert 'WARNING: PREFIX: message4' in warning.getvalue() assert 'WARNING: message5' in warning.getvalue()
def main(argv: List[str] = sys.argv[1:]) -> None: sphinx.locale.setlocale(locale.LC_ALL, '') sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx') translator, _ = sphinx.locale.init([], None) app = DummyApplication(translator) logging.setup(app, sys.stdout, sys.stderr) # type: ignore setup_documenters(app) args = get_parser().parse_args(argv) if args.templates: app.config.templates_path.append(path.abspath(args.templates)) app.config.autosummary_ignore_module_all = not args.respect_module_all # type: ignore generate_autosummary_docs(args.source_file, args.output_dir, '.' + args.suffix, imported_members=args.imported_members, app=app)
def test_parse_name(): logging.setup(app, app._status, app._warning) def verify(objtype, name, result): inst = app.registry.documenters[objtype](directive, name) assert inst.parse_name() assert (inst.modname, inst.objpath, inst.args, inst.retann) == result # for modules verify('module', 'test_autodoc', ('test_autodoc', [], None, None)) verify('module', 'test.test_autodoc', ('test.test_autodoc', [], None, None)) verify('module', 'test(arg)', ('test', [], 'arg', None)) assert 'signature arguments' in app._warning.getvalue() # for functions/classes verify('function', 'test_autodoc.raises', ('test_autodoc', ['raises'], None, None)) verify('function', 'test_autodoc.raises(exc) -> None', ('test_autodoc', ['raises'], 'exc', 'None')) directive.env.temp_data['autodoc:module'] = 'test_autodoc' verify('function', 'raises', ('test_autodoc', ['raises'], None, None)) del directive.env.temp_data['autodoc:module'] directive.env.ref_context['py:module'] = 'test_autodoc' verify('function', 'raises', ('test_autodoc', ['raises'], None, None)) verify('class', 'Base', ('test_autodoc', ['Base'], None, None)) # for members directive.env.ref_context['py:module'] = 'foo' verify('method', 'util.SphinxTestApp.cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None)) directive.env.ref_context['py:module'] = 'util' directive.env.ref_context['py:class'] = 'Foo' directive.env.temp_data['autodoc:class'] = 'SphinxTestApp' verify('method', 'cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None)) verify('method', 'SphinxTestApp.cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None)) # and clean up del directive.env.ref_context['py:module'] del directive.env.ref_context['py:class'] del directive.env.temp_data['autodoc:class']
def test_suppress_warnings(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) app._warncount = 0 # force reset app.config.suppress_warnings = [] warning.truncate(0) logger.warning('message0', type='test') logger.warning('message1', type='test', subtype='logging') logger.warning('message2', type='test', subtype='crash') logger.warning('message3', type='actual', subtype='logging') assert 'message0' in warning.getvalue() assert 'message1' in warning.getvalue() assert 'message2' in warning.getvalue() assert 'message3' in warning.getvalue() assert app._warncount == 4 app.config.suppress_warnings = ['test'] warning.truncate(0) logger.warning('message0', type='test') logger.warning('message1', type='test', subtype='logging') logger.warning('message2', type='test', subtype='crash') logger.warning('message3', type='actual', subtype='logging') assert 'message0' not in warning.getvalue() assert 'message1' not in warning.getvalue() assert 'message2' not in warning.getvalue() assert 'message3' in warning.getvalue() assert app._warncount == 5 app.config.suppress_warnings = ['test.logging'] warning.truncate(0) logger.warning('message0', type='test') logger.warning('message1', type='test', subtype='logging') logger.warning('message2', type='test', subtype='crash') logger.warning('message3', type='actual', subtype='logging') assert 'message0' in warning.getvalue() assert 'message1' not in warning.getvalue() assert 'message2' in warning.getvalue() assert 'message3' in warning.getvalue() assert app._warncount == 8
def test_info_and_warning(app, status, warning): app.verbosity = 2 logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.debug('message1') logger.info('message2') logger.warning('message3') logger.critical('message4') logger.error('message5') assert 'message1' in status.getvalue() assert 'message2' in status.getvalue() assert 'message3' not in status.getvalue() assert 'message4' not in status.getvalue() assert 'message5' not in status.getvalue() assert 'message1' not in warning.getvalue() assert 'message2' not in warning.getvalue() assert 'message3' in warning.getvalue() assert 'message4' in warning.getvalue() assert 'message5' in warning.getvalue()
def test_mangle_docstring_validation_warnings( f, numpydoc_validation_checks, expected_warn, non_warnings, ): app = MockApp() # Set up config for test app.config.numpydoc_validation_checks = numpydoc_validation_checks # Update configuration update_config(app) # Set up logging status, warning = StringIO(), StringIO() logging.setup(app, status, warning) # Run mangle docstrings with the above configuration mangle_docstrings(app, 'function', 'f', f, None, f.__doc__.split('\n')) # Assert that all (and only) expected warnings are logged warnings = warning.getvalue() for w in expected_warn: assert w in warnings for w in non_warnings: assert w not in warnings
def test_skip_warningiserror(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) app.warningiserror = True with logging.skip_warningiserror(): logger.warning('message') # if False, warning raises SphinxWarning exception with pytest.raises(SphinxWarning): with logging.skip_warningiserror(False): logger.warning('message') # It also works during pending_warnings. with logging.pending_warnings(): with logging.skip_warningiserror(): logger.warning('message') with pytest.raises(SphinxWarning): with logging.pending_warnings(): with logging.skip_warningiserror(False): logger.warning('message')
def test_add_is_parallel_allowed(app, status, warning): logging.setup(app, status, warning) assert app.is_parallel_allowed('read') is True assert app.is_parallel_allowed('write') is True assert warning.getvalue() == '' app.setup_extension('read_parallel') assert app.is_parallel_allowed('read') is True assert app.is_parallel_allowed('write') is True assert warning.getvalue() == '' app.extensions.pop('read_parallel') app.setup_extension('write_parallel') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is True assert ("the write_parallel extension does not declare if it is safe " "for parallel reading, assuming it isn't - please " ) in warning.getvalue() app.extensions.pop('write_parallel') warning.truncate(0) # reset warnings app.setup_extension('read_serial') assert app.is_parallel_allowed('read') is False assert "the read_serial extension is not safe for parallel reading" in warning.getvalue( ) warning.truncate(0) # reset warnings assert app.is_parallel_allowed('write') is True assert warning.getvalue() == '' app.extensions.pop('read_serial') app.setup_extension('write_serial') assert app.is_parallel_allowed('read') is False assert app.is_parallel_allowed('write') is False assert ("the write_serial extension does not declare if it is safe " "for parallel reading, assuming it isn't - please " ) in warning.getvalue() app.extensions.pop('write_serial') warning.truncate(0) # reset warnings
def test_advanced(app, status, warning): if sphinx.version_info < (1, 8, 0): pytest.xfail('This should fail on older Sphinx versions') logging.setup(app, status, warning) app.builder.build_all() assert (app.srcdir / 'api').isdir() assert (app.srcdir / 'api' / 'custom.rst').exists() for module in [ 'apidoc_dummy_module.rst', 'apidoc_dummy_package.apidoc_dummy_submodule_a.rst', 'apidoc_dummy_package.apidoc_dummy_submodule_b.rst', 'apidoc_dummy_package._apidoc_private_dummy_submodule.rst', ]: assert (app.srcdir / 'api' / module).exists() assert (app.srcdir / 'api' / 'apidoc_dummy_package.rst').exists() assert not (app.srcdir / 'api' / 'conf.rst').exists() with open(app.srcdir / 'api' / 'apidoc_dummy_package.rst') as fh: package_doc = [x.strip() for x in fh.readlines()] # The 'Module contents' header isn't present if '--module-first' used assert 'Module contents' not in package_doc assert (app.outdir / 'api').isdir() assert (app.outdir / 'api' / 'custom.html').exists() for module in [ 'apidoc_dummy_module.html', 'apidoc_dummy_package.apidoc_dummy_submodule_a.html', 'apidoc_dummy_package.apidoc_dummy_submodule_b.html', 'apidoc_dummy_package._apidoc_private_dummy_submodule.html', ]: assert (app.outdir / 'api' / module).exists() assert (app.outdir / 'api' / 'apidoc_dummy_package.html').exists() assert not (app.outdir / 'api' / 'conf.html').exists() assert not warning.getvalue()
def test_status_iterator(app, status, warning): logging.setup(app, status, warning) # test for old_status_iterator status.truncate(0) yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ')) output = strip_escseq(status.getvalue()) assert 'testing ... hello sphinx world \n' in output assert yields == ['hello', 'sphinx', 'world'] # test for status_iterator (verbosity=0) status.truncate(0) yields = list( status_iterator(['hello', 'sphinx', 'world'], 'testing ... ', length=3, verbosity=0)) output = strip_escseq(status.getvalue()) assert 'testing ... [ 33%] hello \r' in output assert 'testing ... [ 66%] sphinx \r' in output assert 'testing ... [100%] world \r\n' in output assert yields == ['hello', 'sphinx', 'world'] # test for status_iterator (verbosity=1) status.truncate(0) yields = list( status_iterator(['hello', 'sphinx', 'world'], 'testing ... ', length=3, verbosity=1)) output = strip_escseq(status.getvalue()) assert 'testing ... [ 33%] hello\n' in output assert 'testing ... [ 66%] sphinx\n' in output assert 'testing ... [100%] world\n\n' in output assert yields == ['hello', 'sphinx', 'world']
def test_progress_message(app, status, warning): logging.setup(app, status, warning) logger = logging.getLogger(__name__) # standard case with progress_message('testing'): logger.info('blah ', nonl=True) output = strip_escseq(status.getvalue()) assert 'testing... blah done\n' in output # skipping case with progress_message('testing'): raise SkipProgressMessage('Reason: %s', 'error') output = strip_escseq(status.getvalue()) assert 'testing... skipped\nReason: error\n' in output # error case try: with progress_message('testing'): raise except Exception: pass output = strip_escseq(status.getvalue()) assert 'testing... failed\n' in output # decorator @progress_message('testing') def func(): logger.info('in func ', nonl=True) func() output = strip_escseq(status.getvalue()) assert 'testing... in func done\n' in output
def test_verbosity_filter(app, status, warning): # verbosity = 0: INFO app.verbosity = 0 logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.info('message1') logger.verbose('message2') logger.debug('message3') assert 'message1' in status.getvalue() assert 'message2' not in status.getvalue() assert 'message3' not in status.getvalue() assert 'message4' not in status.getvalue() # verbosity = 1: VERBOSE app.verbosity = 1 logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.info('message1') logger.verbose('message2') logger.debug('message3') assert 'message1' in status.getvalue() assert 'message2' in status.getvalue() assert 'message3' not in status.getvalue() assert 'message4' not in status.getvalue() # verbosity = 2: DEBUG app.verbosity = 2 logging.setup(app, status, warning) logger = logging.getLogger(__name__) logger.info('message1') logger.verbose('message2') logger.debug('message3') assert 'message1' in status.getvalue() assert 'message2' in status.getvalue() assert 'message3' in status.getvalue() assert 'message4' not in status.getvalue()
def test_generate(): logging.setup(app, app._status, app._warning) def assert_warns(warn_str, objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) assert len(directive.result) == 0, directive.result assert warn_str in app._warning.getvalue() app._warning.truncate(0) def assert_works(objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) assert directive.result # print '\n'.join(directive.result) assert app._warning.getvalue() == '' del directive.result[:] def assert_processes(items, objtype, name, **kw): del processed_docstrings[:] del processed_signatures[:] assert_works(objtype, name, **kw) assert set(processed_docstrings) | set(processed_signatures) == set(items) def assert_result_contains(item, objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) # print '\n'.join(directive.result) assert app._warning.getvalue() == '' assert item in directive.result del directive.result[:] def assert_order(items, objtype, name, member_order, **kw): inst = app.registry.documenters[objtype](directive, name) inst.options.member_order = member_order inst.generate(**kw) assert app._warning.getvalue() == '' items = list(reversed(items)) lineiter = iter(directive.result) # for line in directive.result: # if line.strip(): # print repr(line) while items: item = items.pop() for line in lineiter: if line == item: break else: # ran out of items! assert False, ('item %r not found in result or not in the ' ' correct order' % item) del directive.result[:] options.members = [] # no module found? assert_warns("import for autodocumenting 'foobar'", 'function', 'foobar', more_content=None) # importing assert_warns("failed to import module 'test_foobar'", 'module', 'test_foobar', more_content=None) # attributes missing assert_warns("failed to import function 'foobar' from module 'util'", 'function', 'util.foobar', more_content=None) # method missing assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc_py35';", 'method', 'test_autodoc_py35.Class.foobar', more_content=None) # test auto and given content mixing directive.env.ref_context['py:module'] = 'test_autodoc_py35' assert_result_contains(' Function.', 'method', 'Class.meth') add_content = ViewList() add_content.append('Content.', '', 0) assert_result_contains(' Function.', 'method', 'Class.meth', more_content=add_content) assert_result_contains(' Content.', 'method', 'Class.meth', more_content=add_content) # test check_module inst = FunctionDocumenter(directive, 'add_documenter') inst.generate(check_module=True) assert len(directive.result) == 0 # assert that exceptions can be documented assert_works('exception', 'test_autodoc_py35.CustomEx', all_members=True) assert_works('exception', 'test_autodoc_py35.CustomEx') # test diverse inclusion settings for members should = [('class', 'test_autodoc_py35.Class')] assert_processes(should, 'class', 'Class') should.extend([('method', 'test_autodoc_py35.Class.meth')]) options.members = ['meth'] options.exclude_members = set(['excludemeth']) assert_processes(should, 'class', 'Class') should.extend([('attribute', 'test_autodoc_py35.Class.prop'), ('attribute', 'test_autodoc_py35.Class.descr'), ('attribute', 'test_autodoc_py35.Class.attr'), ('attribute', 'test_autodoc_py35.Class.docattr'), ('attribute', 'test_autodoc_py35.Class.udocattr'), ('attribute', 'test_autodoc_py35.Class.mdocattr'), ('attribute', 'test_autodoc_py35.Class.inst_attr_comment'), ('attribute', 'test_autodoc_py35.Class.inst_attr_inline'), ('attribute', 'test_autodoc_py35.Class.inst_attr_string'), ('method', 'test_autodoc_py35.Class.moore'), ]) if six.PY3 and sys.version_info[:2] >= (3, 5): should.extend([ ('method', 'test_autodoc_py35.Class.do_coroutine'), ]) options.members = ALL assert_processes(should, 'class', 'Class') options.undoc_members = True should.extend((('attribute', 'test_autodoc_py35.Class.skipattr'), ('method', 'test_autodoc_py35.Class.undocmeth'), ('method', 'test_autodoc_py35.Class.roger'))) assert_processes(should, 'class', 'Class') options.inherited_members = True should.append(('method', 'test_autodoc_py35.Class.inheritedmeth')) assert_processes(should, 'class', 'Class') # test special members options.special_members = ['__special1__'] should.append(('method', 'test_autodoc_py35.Class.__special1__')) assert_processes(should, 'class', 'Class') options.special_members = ALL should.append(('method', 'test_autodoc_py35.Class.__special2__')) assert_processes(should, 'class', 'Class') options.special_members = False
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides=None, status=sys.stdout, warning=sys.stderr, freshenv=False, warningiserror=False, tags=None, verbosity=0, parallel=0, keep_going=False): # type: (str, str, str, str, str, Dict, IO, IO, bool, bool, List[str], int, int, bool) -> None # NOQA self.phase = BuildPhase.INITIALIZATION self.verbosity = verbosity self.extensions = {} # type: Dict[str, Extension] self.builder = None # type: Builder self.env = None # type: BuildEnvironment self.project = None # type: Project self.registry = SphinxComponentRegistry() self.html_themes = {} # type: Dict[str, str] # validate provided directories self.srcdir = abspath(srcdir) self.outdir = abspath(outdir) self.doctreedir = abspath(doctreedir) self.confdir = confdir if self.confdir: # confdir is optional self.confdir = abspath(self.confdir) if not path.isfile(path.join(self.confdir, 'conf.py')): raise ApplicationError(__("config directory doesn't contain a " "conf.py file (%s)") % confdir) if not path.isdir(self.srcdir): raise ApplicationError(__('Cannot find source directory (%s)') % self.srcdir) if self.srcdir == self.outdir: raise ApplicationError(__('Source directory and destination ' 'directory cannot be identical')) self.parallel = parallel if status is None: self._status = StringIO() # type: IO self.quiet = True else: self._status = status self.quiet = False if warning is None: self._warning = StringIO() # type: IO else: self._warning = warning self._warncount = 0 self.keep_going = warningiserror and keep_going if self.keep_going: self.warningiserror = False else: self.warningiserror = warningiserror logging.setup(self, self._status, self._warning) self.events = EventManager() # keep last few messages for traceback # This will be filled by sphinx.util.logging.LastMessagesWriter self.messagelog = deque(maxlen=10) # type: deque # say hello to the world logger.info(bold(__('Running Sphinx v%s') % sphinx.__display_version__)) # status code for command-line application self.statuscode = 0 # read config self.tags = Tags(tags) if self.confdir is None: self.config = Config({}, confoverrides or {}) else: self.config = Config.read(self.confdir, confoverrides or {}, self.tags) # initialize some limited config variables before initialize i18n and loading # extensions self.config.pre_init_values() # set up translation infrastructure self._init_i18n() # check the Sphinx version if requested if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__: raise VersionRequirementError( __('This project needs at least Sphinx v%s and therefore cannot ' 'be built with this version.') % self.config.needs_sphinx) # set confdir to srcdir if -C given (!= no confdir); a few pieces # of code expect a confdir to be set if self.confdir is None: self.confdir = self.srcdir # load all built-in extension modules for extension in builtin_extensions: self.setup_extension(extension) # load all user-given extension modules for extension in self.config.extensions: self.setup_extension(extension) # preload builder module (before init config values) self.preload_builder(buildername) if not path.isdir(outdir): with progress_message(__('making output directory')): ensuredir(outdir) # the config file itself can be an extension if self.config.setup: prefix = __('while setting up extension %s:') % "conf.py" with prefixed_warnings(prefix): if callable(self.config.setup): self.config.setup(self) else: raise ConfigError( __("'setup' as currently defined in conf.py isn't a Python callable. " "Please modify its definition to make it a callable function. " "This is needed for conf.py to behave as a Sphinx extension.") ) # now that we know all config values, collect them from conf.py self.config.init_values() self.emit('config-inited', self.config) # create the project self.project = Project(self.srcdir, self.config.source_suffix) # create the builder self.builder = self.create_builder(buildername) # set up the build environment self._init_env(freshenv) # set up the builder self._init_builder()
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides=None, status=sys.stdout, warning=sys.stderr, freshenv=False, warningiserror=False, tags=None, verbosity=0, parallel=0, keep_going=False): # type: (str, str, str, str, str, Dict, IO, IO, bool, bool, List[str], int, int, bool) -> None # NOQA self.phase = BuildPhase.INITIALIZATION self.verbosity = verbosity self.extensions = {} # type: Dict[str, Extension] self.builder = None # type: Builder self.env = None # type: BuildEnvironment self.project = None # type: Project self.registry = SphinxComponentRegistry() self.html_themes = {} # type: Dict[str, str] # validate provided directories self.srcdir = abspath(srcdir) self.outdir = abspath(outdir) self.doctreedir = abspath(doctreedir) self.confdir = confdir if self.confdir: # confdir is optional self.confdir = abspath(self.confdir) if not path.isfile(path.join(self.confdir, 'conf.py')): raise ApplicationError( __("config directory doesn't contain a " "conf.py file (%s)") % confdir) if not path.isdir(self.srcdir): raise ApplicationError( __('Cannot find source directory (%s)') % self.srcdir) if self.srcdir == self.outdir: raise ApplicationError( __('Source directory and destination ' 'directory cannot be identical')) self.parallel = parallel if status is None: self._status = StringIO() # type: IO self.quiet = True else: self._status = status self.quiet = False if warning is None: self._warning = StringIO() # type: IO else: self._warning = warning self._warncount = 0 self.keep_going = warningiserror and keep_going if self.keep_going: self.warningiserror = False else: self.warningiserror = warningiserror logging.setup(self, self._status, self._warning) self.events = EventManager(self) # keep last few messages for traceback # This will be filled by sphinx.util.logging.LastMessagesWriter self.messagelog = deque(maxlen=10) # type: deque # say hello to the world logger.info(bold( __('Running Sphinx v%s') % sphinx.__display_version__)) # notice for parallel build on macOS and py38+ if sys.version_info > ( 3, 8) and platform.system() == 'Darwin' and parallel > 1: logger.info( bold( __("For security reason, parallel mode is disabled on macOS and " "python3.8 and above. For more details, please read " "https://github.com/sphinx-doc/sphinx/issues/6803"))) # status code for command-line application self.statuscode = 0 # read config self.tags = Tags(tags) if self.confdir is None: self.config = Config({}, confoverrides or {}) else: self.config = Config.read(self.confdir, confoverrides or {}, self.tags) # initialize some limited config variables before initialize i18n and loading # extensions self.config.pre_init_values() # set up translation infrastructure self._init_i18n() # check the Sphinx version if requested if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__: raise VersionRequirementError( __('This project needs at least Sphinx v%s and therefore cannot ' 'be built with this version.') % self.config.needs_sphinx) # set confdir to srcdir if -C given (!= no confdir); a few pieces # of code expect a confdir to be set if self.confdir is None: self.confdir = self.srcdir # load all built-in extension modules for extension in builtin_extensions: self.setup_extension(extension) # load all user-given extension modules for extension in self.config.extensions: self.setup_extension(extension) # preload builder module (before init config values) self.preload_builder(buildername) if not path.isdir(outdir): with progress_message(__('making output directory')): ensuredir(outdir) # the config file itself can be an extension if self.config.setup: prefix = __('while setting up extension %s:') % "conf.py" with prefixed_warnings(prefix): if callable(self.config.setup): self.config.setup(self) else: raise ConfigError( __("'setup' as currently defined in conf.py isn't a Python callable. " "Please modify its definition to make it a callable function. " "This is needed for conf.py to behave as a Sphinx extension." )) # now that we know all config values, collect them from conf.py self.config.init_values() self.events.emit('config-inited', self.config) # create the project self.project = Project(self.srcdir, self.config.source_suffix) # create the builder self.builder = self.create_builder(buildername) # set up the build environment self._init_env(freshenv) # set up the builder self._init_builder()
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides=None, status=sys.stdout, warning=sys.stderr, freshenv=False, warningiserror=False, tags=None, verbosity=0, parallel=0): # type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None # NOQA self.verbosity = verbosity self.extensions = {} # type: Dict[unicode, Extension] self._setting_up_extension = ['?'] # type: List[unicode] self.builder = None # type: Builder self.env = None # type: BuildEnvironment self.registry = SphinxComponentRegistry() self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA self.post_transforms = [] # type: List[Transform] self.html_themes = {} # type: Dict[unicode, unicode] self.srcdir = srcdir self.confdir = confdir self.outdir = outdir self.doctreedir = doctreedir self.parallel = parallel if status is None: self._status = cStringIO() # type: IO self.quiet = True else: self._status = status self.quiet = False if warning is None: self._warning = cStringIO() # type: IO else: self._warning = warning self._warncount = 0 self.warningiserror = warningiserror logging.setup(self, self._status, self._warning) self.events = EventManager() # keep last few messages for traceback # This will be filled by sphinx.util.logging.LastMessagesWriter self.messagelog = deque(maxlen=10) # type: deque # say hello to the world logger.info(bold('Running Sphinx v%s' % sphinx.__display_version__)) # status code for command-line application self.statuscode = 0 if not path.isdir(outdir): logger.info('making output directory...') os.makedirs(outdir) # read config self.tags = Tags(tags) self.config = Config(confdir, CONFIG_FILENAME, confoverrides or {}, self.tags) self.config.check_unicode() # defer checking types until i18n has been initialized # initialize some limited config variables before initialize i18n and loading # extensions self.config.pre_init_values() # set up translation infrastructure self._init_i18n() # check the Sphinx version if requested if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__: raise VersionRequirementError( __('This project needs at least Sphinx v%s and therefore cannot ' 'be built with this version.') % self.config.needs_sphinx) # set confdir to srcdir if -C given (!= no confdir); a few pieces # of code expect a confdir to be set if self.confdir is None: self.confdir = self.srcdir # load all built-in extension modules for extension in builtin_extensions: self.setup_extension(extension) # load all user-given extension modules for extension in self.config.extensions: self.setup_extension(extension) # preload builder module (before init config values) self.preload_builder(buildername) # the config file itself can be an extension if self.config.setup: self._setting_up_extension = ['conf.py'] # py31 doesn't have 'callable' function for below check if hasattr(self.config.setup, '__call__'): self.config.setup(self) else: raise ConfigError( __("'setup' as currently defined in conf.py isn't a Python callable. " "Please modify its definition to make it a callable function. This is " "needed for conf.py to behave as a Sphinx extension.") ) # now that we know all config values, collect them from conf.py self.config.init_values() # check extension versions if requested verify_required_extensions(self, self.config.needs_extensions) # check primary_domain if requested primary_domain = self.config.primary_domain if primary_domain and not self.registry.has_domain(primary_domain): logger.warning(__('primary_domain %r not found, ignored.'), primary_domain) # create the builder self.builder = self.create_builder(buildername) # check all configuration values for permissible types self.config.check_types() # set up source_parsers self._init_source_parsers() # set up the build environment self._init_env(freshenv) # set up the builder self._init_builder() # set up the enumerable nodes self._init_enumerable_nodes()
def test_generate(): logging.setup(app, app._status, app._warning) def assert_warns(warn_str, objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) assert len(directive.result) == 0, directive.result assert warn_str in app._warning.getvalue() app._warning.truncate(0) def assert_works(objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) assert directive.result # print '\n'.join(directive.result) assert app._warning.getvalue() == '' del directive.result[:] def assert_processes(items, objtype, name, **kw): del processed_docstrings[:] del processed_signatures[:] assert_works(objtype, name, **kw) assert set(processed_docstrings) | set(processed_signatures) == set(items) def assert_result_contains(item, objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) # print '\n'.join(directive.result) assert app._warning.getvalue() == '' assert item in directive.result del directive.result[:] def assert_order(items, objtype, name, member_order, **kw): inst = app.registry.documenters[objtype](directive, name) inst.options.member_order = member_order inst.generate(**kw) assert app._warning.getvalue() == '' items = list(reversed(items)) lineiter = iter(directive.result) # for line in directive.result: # if line.strip(): # print repr(line) while items: item = items.pop() for line in lineiter: if line == item: break else: # ran out of items! assert False, ('item %r not found in result or not in the ' ' correct order' % item) del directive.result[:] options.members = [] # no module found? assert_warns("import for autodocumenting 'foobar'", 'function', 'foobar', more_content=None) # importing assert_warns("failed to import module 'test_foobar'", 'module', 'test_foobar', more_content=None) # attributes missing assert_warns("failed to import function 'foobar' from module 'util'", 'function', 'util.foobar', more_content=None) # method missing assert_warns("failed to import method 'Class.foobar' from module 'target';", 'method', 'target.Class.foobar', more_content=None) # test auto and given content mixing directive.env.ref_context['py:module'] = 'target' assert_result_contains(' Function.', 'method', 'Class.meth') add_content = ViewList() add_content.append('Content.', '', 0) assert_result_contains(' Function.', 'method', 'Class.meth', more_content=add_content) assert_result_contains(' Content.', 'method', 'Class.meth', more_content=add_content) # test check_module inst = FunctionDocumenter(directive, 'add_documenter') inst.generate(check_module=True) assert len(directive.result) == 0 # assert that exceptions can be documented assert_works('exception', 'target.CustomEx', all_members=True) assert_works('exception', 'target.CustomEx') # test diverse inclusion settings for members should = [('class', 'target.Class')] assert_processes(should, 'class', 'Class') should.extend([('method', 'target.Class.meth')]) options.members = ['meth'] options.exclude_members = set(['excludemeth']) assert_processes(should, 'class', 'Class') should.extend([('attribute', 'target.Class.prop'), ('attribute', 'target.Class.descr'), ('attribute', 'target.Class.attr'), ('attribute', 'target.Class.docattr'), ('attribute', 'target.Class.udocattr'), ('attribute', 'target.Class.mdocattr'), ('attribute', 'target.Class.inst_attr_comment'), ('attribute', 'target.Class.inst_attr_inline'), ('attribute', 'target.Class.inst_attr_string'), ('method', 'target.Class.moore'), ]) options.members = ALL assert_processes(should, 'class', 'Class') options.undoc_members = True should.extend((('attribute', 'target.Class.skipattr'), ('method', 'target.Class.undocmeth'), ('method', 'target.Class.roger'))) assert_processes(should, 'class', 'Class') options.inherited_members = True should.append(('method', 'target.Class.inheritedmeth')) should.append(('method', 'target.Class.inheritedclassmeth')) should.append(('method', 'target.Class.inheritedstaticmeth')) assert_processes(should, 'class', 'Class') # test special members options.special_members = ['__special1__'] should.append(('method', 'target.Class.__special1__')) assert_processes(should, 'class', 'Class') options.special_members = ALL should.append(('method', 'target.Class.__special2__')) assert_processes(should, 'class', 'Class') options.special_members = False options.members = [] # test module flags assert_result_contains('.. py:module:: target', 'module', 'target') options.synopsis = 'Synopsis' assert_result_contains(' :synopsis: Synopsis', 'module', 'target') options.deprecated = True assert_result_contains(' :deprecated:', 'module', 'target') options.platform = 'Platform' assert_result_contains(' :platform: Platform', 'module', 'target') # test if __all__ is respected for modules options.members = ALL assert_result_contains('.. py:class:: Class(arg)', 'module', 'target') try: assert_result_contains('.. py:exception:: CustomEx', 'module', 'target') except AssertionError: pass else: assert False, 'documented CustomEx which is not in __all__' # test ignore-module-all options.ignore_module_all = True assert_result_contains('.. py:class:: Class(arg)', 'module', 'target') assert_result_contains('.. py:exception:: CustomEx', 'module', 'target') # test noindex flag options.members = [] options.noindex = True assert_result_contains(' :noindex:', 'module', 'target') assert_result_contains(' :noindex:', 'class', 'Base') # okay, now let's get serious about mixing Python and C signature stuff assert_result_contains('.. py:class:: CustomDict', 'class', 'CustomDict', all_members=True) # test inner class handling assert_processes([('class', 'target.Outer'), ('class', 'target.Outer.Inner'), ('method', 'target.Outer.Inner.meth')], 'class', 'Outer', all_members=True) # test descriptor docstrings assert_result_contains(' Descriptor instance docstring.', 'attribute', 'target.Class.descr') # test generation for C modules (which have no source file) directive.env.ref_context['py:module'] = 'time' assert_processes([('function', 'time.asctime')], 'function', 'asctime') assert_processes([('function', 'time.asctime')], 'function', 'asctime') # test autodoc_member_order == 'source' directive.env.ref_context['py:module'] = 'target' options.private_members = True if PY3: roger_line = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)' else: roger_line = ' .. py:classmethod:: Class.roger(a, e=5, f=6)' assert_order(['.. py:class:: Class(arg)', ' .. py:attribute:: Class.descr', ' .. py:method:: Class.meth()', ' .. py:method:: Class.undocmeth()', ' .. py:attribute:: Class.attr', ' .. py:attribute:: Class.prop', ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', roger_line, ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class._private_inst_attr', ' .. py:classmethod:: Class.inheritedclassmeth()', ' .. py:method:: Class.inheritedmeth()', ' .. py:staticmethod:: Class.inheritedstaticmeth()', ], 'class', 'Class', member_order='bysource', all_members=True) del directive.env.ref_context['py:module'] # test attribute initialized to class instance from other module directive.env.temp_data['autodoc:class'] = 'target.Class' assert_result_contains(u' should be documented as well - s\xfc\xdf', 'attribute', 'mdocattr') del directive.env.temp_data['autodoc:class'] # test autodoc_docstring_signature assert_result_contains( '.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method', 'target.DocstringSig.meth') assert_result_contains( ' rest of docstring', 'method', 'target.DocstringSig.meth') assert_result_contains( '.. py:method:: DocstringSig.meth2()', 'method', 'target.DocstringSig.meth2') assert_result_contains( ' indented line', 'method', 'target.DocstringSig.meth2') assert_result_contains( '.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method', 'target.Class.moore') # test new attribute documenter behavior directive.env.ref_context['py:module'] = 'target' options.undoc_members = True assert_processes([('class', 'target.AttCls'), ('attribute', 'target.AttCls.a1'), ('attribute', 'target.AttCls.a2'), ], 'class', 'AttCls') assert_result_contains( ' :annotation: = hello world', 'attribute', 'AttCls.a1') assert_result_contains( ' :annotation: = None', 'attribute', 'AttCls.a2') # test explicit members with instance attributes del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:module'] directive.env.ref_context['py:module'] = 'target' options.inherited_members = False options.undoc_members = False options.members = ALL assert_processes([ ('class', 'target.InstAttCls'), ('attribute', 'target.InstAttCls.ca1'), ('attribute', 'target.InstAttCls.ca2'), ('attribute', 'target.InstAttCls.ca3'), ('attribute', 'target.InstAttCls.ia1'), ('attribute', 'target.InstAttCls.ia2'), ], 'class', 'InstAttCls') del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:module'] options.members = ['ca1', 'ia1'] assert_processes([ ('class', 'target.InstAttCls'), ('attribute', 'target.InstAttCls.ca1'), ('attribute', 'target.InstAttCls.ia1'), ], 'class', 'InstAttCls') del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:module'] del directive.env.ref_context['py:module'] # test members with enum attributes directive.env.ref_context['py:module'] = 'target' options.inherited_members = False options.undoc_members = False options.members = ALL assert_processes([ ('class', 'target.EnumCls'), ('attribute', 'target.EnumCls.val1'), ('attribute', 'target.EnumCls.val2'), ('attribute', 'target.EnumCls.val3'), ], 'class', 'EnumCls') assert_result_contains( ' :annotation: = 12', 'attribute', 'EnumCls.val1') assert_result_contains( ' :annotation: = 23', 'attribute', 'EnumCls.val2') assert_result_contains( ' :annotation: = 34', 'attribute', 'EnumCls.val3') del directive.env.temp_data['autodoc:class'] del directive.env.temp_data['autodoc:module'] # test descriptor class documentation options.members = ['CustomDataDescriptor', 'CustomDataDescriptor2'] assert_result_contains('.. py:class:: CustomDataDescriptor(doc)', 'module', 'target') assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()', 'module', 'target') assert_result_contains('.. py:class:: CustomDataDescriptor2(doc)', 'module', 'target') # test mocked module imports options.members = ['TestAutodoc'] options.undoc_members = False assert_result_contains('.. py:class:: TestAutodoc', 'module', 'autodoc_missing_imports') assert_result_contains(' .. py:method:: TestAutodoc.decoratedMethod()', 'module', 'autodoc_missing_imports') options.members = ['decoratedFunction'] assert_result_contains('.. py:function:: decoratedFunction()', 'module', 'autodoc_missing_imports')