diff --git a/tests/pyexample/Makefile b/tests/python/pyexample/Makefile similarity index 100% rename from tests/pyexample/Makefile rename to tests/python/pyexample/Makefile diff --git a/tests/pyexample/conf.py b/tests/python/pyexample/conf.py similarity index 100% rename from tests/pyexample/conf.py rename to tests/python/pyexample/conf.py diff --git a/tests/pyexample/example/example.py b/tests/python/pyexample/example/example.py similarity index 100% rename from tests/pyexample/example/example.py rename to tests/python/pyexample/example/example.py diff --git a/tests/pyexample/index.rst b/tests/python/pyexample/index.rst similarity index 100% rename from tests/pyexample/index.rst rename to tests/python/pyexample/index.rst diff --git a/tests/pyexample/manualapi.rst b/tests/python/pyexample/manualapi.rst similarity index 100% rename from tests/pyexample/manualapi.rst rename to tests/python/pyexample/manualapi.rst diff --git a/tests/python/pypackagecomplex/complex/__init__.py b/tests/python/pypackagecomplex/complex/__init__.py new file mode 100644 index 0000000..c348fee --- /dev/null +++ b/tests/python/pypackagecomplex/complex/__init__.py @@ -0,0 +1,6 @@ +from .subpackage import public_chain +from .subpackage.submodule import public_multiple_imports + +def module_level_method(foo, bar): + """A module level method""" + pass diff --git a/tests/pypackageexample/example/_private_module.py b/tests/python/pypackagecomplex/complex/_private_module.py similarity index 100% rename from tests/pypackageexample/example/_private_module.py rename to tests/python/pypackagecomplex/complex/_private_module.py diff --git a/tests/pypackageexample/example/foo.py b/tests/python/pypackagecomplex/complex/foo.py similarity index 100% rename from tests/pypackageexample/example/foo.py rename to tests/python/pypackagecomplex/complex/foo.py diff --git a/tests/python/pypackagecomplex/complex/subpackage/__init__.py b/tests/python/pypackagecomplex/complex/subpackage/__init__.py new file mode 100644 index 0000000..d4bdba7 --- /dev/null +++ b/tests/python/pypackagecomplex/complex/subpackage/__init__.py @@ -0,0 +1,10 @@ +from .submodule import public_chain +from .submodule import _private_made_public as now_public_function +from .submodule import public_multiple_imports + +def module_level_method(foo, bar): + """A module level method""" + pass +def module_level_method(foo, bar): + """A module level method""" + pass diff --git a/tests/python/pypackagecomplex/complex/subpackage/submodule.py b/tests/python/pypackagecomplex/complex/subpackage/submodule.py new file mode 100644 index 0000000..c074c84 --- /dev/null +++ b/tests/python/pypackagecomplex/complex/subpackage/submodule.py @@ -0,0 +1,11 @@ +def public_chain(): + """Part of a resolution chain.""" + return 5 + +def _private_made_public(): + """A private function made public by import.""" + return 5 + +def public_multiple_imports(): + """A public function imported in multiple places.""" + return 5 \ No newline at end of file diff --git a/tests/python/pypackagecomplex/conf.py b/tests/python/pypackagecomplex/conf.py new file mode 100644 index 0000000..ab26b0b --- /dev/null +++ b/tests/python/pypackagecomplex/conf.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'pypackagecomplex' +copyright = u'2015, rtfd' +author = u'rtfd' +version = '0.1' +release = '0.1' +language = None +exclude_patterns = ['_build'] +pygments_style = 'sphinx' +todo_include_todos = False +html_theme = 'alabaster' +html_static_path = ['_static'] +htmlhelp_basename = 'pypackagecomplexdoc' +extensions = ['autoapi.extension'] +autoapi_type = 'python' +autoapi_dirs = ['complex'] +autoapi_file_pattern = '*.py' diff --git a/tests/pypackageexample/index.rst b/tests/python/pypackagecomplex/index.rst similarity index 100% rename from tests/pypackageexample/index.rst rename to tests/python/pypackagecomplex/index.rst diff --git a/tests/pypackageexample/conf.py b/tests/python/pypackageexample/conf.py similarity index 100% rename from tests/pypackageexample/conf.py rename to tests/python/pypackageexample/conf.py diff --git a/tests/pypackageexample/example/__init__.py b/tests/python/pypackageexample/example/__init__.py similarity index 100% rename from tests/pypackageexample/example/__init__.py rename to tests/python/pypackageexample/example/__init__.py diff --git a/tests/python/pypackageexample/example/_private_module.py b/tests/python/pypackageexample/example/_private_module.py new file mode 100644 index 0000000..537db64 --- /dev/null +++ b/tests/python/pypackageexample/example/_private_module.py @@ -0,0 +1,6 @@ +class PrivateClass(object): + """A private class with public facing methods.""" + + def public_method(): + """This is public.""" + return 5 diff --git a/tests/python/pypackageexample/example/foo.py b/tests/python/pypackageexample/example/foo.py new file mode 100644 index 0000000..8bb8d32 --- /dev/null +++ b/tests/python/pypackageexample/example/foo.py @@ -0,0 +1,65 @@ +"""Example module + +This is a description +""" + + +class Foo(object): + + class_var = 42 #: Class var docstring + + another_class_var = 42 + """Another class var docstring""" + + class Meta(object): + """A nested class just to test things out""" + + @classmethod + def foo(): + """The foo class method""" + return True + + def method_okay(self, foo=None, bar=None): + """This method should parse okay""" + return True + + def method_multiline(self, foo=None, bar=None, + baz=None): + """This is on multiple lines, but should parse okay too + + pydocstyle gives us lines of source. Test if this means that multiline + definitions are covered in the way we're anticipating here + """ + return True + + def method_tricky(self, foo=None, bar=dict(foo=1, bar=2)): + """This will likely fail our argument testing + + We parse naively on commas, so the nested dictionary will throw this off + """ + return True + + def method_sphinx_docs(self, foo, bar=0): + """This method is documented with sphinx style docstrings. + + :param foo: The first argument. + :type foo: int + + :param int bar: The second argument. + + :returns: The sum of foo and bar. + :rtype: int + """ + return foo + bar + + def method_google_docs(self, foo, bar=0): + """This method is documented with google style docstrings. + + Args: + foo (int): The first argument. + bar (int): The second argument. + + Returns: + int: The sum of foo and bar. + """ + return foo + bar diff --git a/tests/python/pypackageexample/index.rst b/tests/python/pypackageexample/index.rst new file mode 100644 index 0000000..a4c6a2c --- /dev/null +++ b/tests/python/pypackageexample/index.rst @@ -0,0 +1,25 @@ +.. pypackageexample documentation master file, created by + sphinx-quickstart on Fri May 29 13:34:37 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pypackageexample's documentation! +===================================== + +.. toctree:: + + autoapi/index + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/tests/test_python_parser.py b/tests/python/test_parser.py similarity index 100% rename from tests/test_python_parser.py rename to tests/python/test_parser.py diff --git a/tests/python/test_pyintegration.py b/tests/python/test_pyintegration.py new file mode 100644 index 0000000..fd0bacd --- /dev/null +++ b/tests/python/test_pyintegration.py @@ -0,0 +1,200 @@ +import io +import os +import shutil + +import pytest +import sphinx +from sphinx.application import Sphinx + + +@pytest.fixture(scope='class') +def builder(): + cwd = os.getcwd() + + def build(test_dir, confoverrides=None): + os.chdir('tests/python/{0}'.format(test_dir)) + app = Sphinx( + srcdir='.', + confdir='.', + outdir='_build/text', + doctreedir='_build/.doctrees', + buildername='text', + confoverrides=confoverrides, + ) + app.build(force_all=True) + + yield build + + try: + shutil.rmtree('_build') + finally: + os.chdir(cwd) + + +class TestSimpleModule(object): + + @pytest.fixture(autouse=True, scope='class') + def built(self, builder): + builder('pyexample') + + def test_integration(self): + self.check_integration( + '_build/text/autoapi/example/index.txt', + ) + + def test_manual_directives(self): + # The manual directives should contain the same information + self.check_integration( + '_build/text/manualapi.txt', + ) + + def check_integration(self, example_path): + with io.open(example_path, encoding='utf8') as example_handle: + example_file = example_handle.read() + + assert 'class example.Foo' in example_file + assert 'class Meta' in example_file + assert 'attr2' in example_file + assert 'This is the docstring of an instance attribute.' in example_file + assert 'method_okay(self, foo=None, bar=None)' in example_file + assert 'method_multiline(self, foo=None, bar=None, baz=None)' in example_file + assert 'method_tricky(self, foo=None, bar=dict(foo=1, bar=2))' in example_file + + # Are constructor arguments from the class docstring parsed? + assert 'Set an attribute' in example_file + + # "self" should not be included in constructor arguments + assert 'Foo(self' not in example_file + + assert not os.path.exists('_build/text/autoapi/method_multiline') + + index_path = '_build/text/index.txt' + with io.open(index_path, encoding='utf8') as index_handle: + index_file = index_handle.read() + + assert 'Sphinx AutoAPI Index' in index_file + + assert 'Foo' in index_file + assert 'Meta' in index_file + + @pytest.mark.skipif(sphinx.version_info < (1, 4), + reason="Cannot override extensions in Sphinx 1.3") + def test_napoleon_integration_not_loaded(self, builder): + + example_path = '_build/text/autoapi/example/index.txt' + with io.open(example_path, encoding='utf8') as example_handle: + example_file = example_handle.read() + + # Check that docstrings are not transformed without napoleon loaded + assert 'Args' in example_file + + assert 'Returns' in example_file + + +@pytest.mark.skipif(sphinx.version_info < (1, 4), + reason="Cannot override extensions in Sphinx 1.3") +def test_napoleon_integration_loaded(builder): + confoverrides = { + 'extensions': [ + 'autoapi.extension', + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + ], + } + + builder('pyexample', confoverrides=confoverrides) + + example_path = '_build/text/autoapi/example/index.txt' + with io.open(example_path, encoding='utf8') as example_handle: + example_file = example_handle.read() + + assert 'Parameters' in example_file + + assert 'Return type' in example_file + + assert 'Args' not in example_file + + +class TestSimplePackage(object): + + @pytest.fixture(autouse=True, scope='class') + def built(self, builder): + builder('pypackageexample') + + def test_integration_with_package(self, builder): + + example_path = '_build/text/autoapi/example/index.txt' + with io.open(example_path, encoding='utf8') as example_handle: + example_file = example_handle.read() + + assert 'example.foo' in example_file + assert 'example.module_level_method(foo, bar)' in example_file + + example_foo_path = '_build/text/autoapi/example/foo/index.txt' + with io.open(example_foo_path, encoding='utf8') as example_foo_handle: + example_foo_file = example_foo_handle.read() + + assert 'class example.foo.Foo' in example_foo_file + assert 'method_okay(self, foo=None, bar=None)' in example_foo_file + + index_path = '_build/text/index.txt' + with io.open(index_path, encoding='utf8') as index_handle: + index_file = index_handle.read() + + assert 'Sphinx AutoAPI Index' in index_file + assert 'example.foo' in index_file + assert 'Foo' in index_file + assert 'module_level_method' in index_file + + +def _test_class_content(builder, class_content): + confoverrides = { + 'autoapi_python_class_content': class_content, + } + + builder('pyexample', confoverrides=confoverrides) + + example_path = '_build/text/autoapi/example/index.txt' + with io.open(example_path, encoding='utf8') as example_handle: + example_file = example_handle.read() + + if class_content == 'init': + assert 'Can we parse arguments' not in example_file + else: + assert 'Can we parse arguments' in example_file + + if class_content not in ('both', 'init'): + assert 'Constructor docstring' not in example_file + else: + assert 'Constructor docstring' in example_file + + +def test_class_class_content(builder): + _test_class_content(builder, 'class') + + +def test_both_class_content(builder): + _test_class_content(builder, 'both') + + +def test_init_class_content(builder): + _test_class_content(builder, 'init') + + +def test_hiding_private_members(builder): + confoverrides = { + 'autoapi_options': ['members', 'undoc-members', 'special-members'], + } + builder('pypackageexample', confoverrides=confoverrides) + + example_path = '_build/text/autoapi/example/index.txt' + with io.open(example_path, encoding='utf8') as example_handle: + example_file = example_handle.read() + + assert 'private' not in example_file + + private_path = '_build/text/autoapi/example/_private_module/index.txt' + with io.open(private_path, encoding='utf8') as private_handle: + private_file = private_handle.read() + + assert 'public_method' in private_file diff --git a/tests/test_integration.py b/tests/test_integration.py index 1708686..09f11ec 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -69,228 +69,6 @@ class GoTests(LanguageIntegrationTests): ) -class PythonTests(LanguageIntegrationTests): - - def test_integration(self): - self.check_integration( - '_build/text/autoapi/example/index.txt' - ) - - def test_manual_directives(self): - # The manual directives should contain the same information - self.check_integration( - '_build/text/manualapi.txt' - ) - - def check_integration(self, example_path): - with sphinx_build('pyexample'): - with io.open(example_path, encoding='utf8') as example_handle: - example_file = example_handle.read() - self.assertIn( - 'class example.Foo', - example_file - ) - self.assertIn( - 'class Meta', - example_file - ) - self.assertIn( - 'attr2', - example_file - ) - self.assertIn( - 'This is the docstring of an instance attribute.', - example_file - ) - self.assertIn( - 'method_okay(self, foo=None, bar=None)', - example_file - ) - self.assertIn( - 'method_multiline(self, foo=None, bar=None, baz=None)', - example_file - ) - self.assertIn( - 'method_tricky(self, foo=None, bar=dict(foo=1, bar=2))', - example_file - ) - # Are constructor arguments from the class docstring parsed? - self.assertIn( - 'Set an attribute', - example_file - ) - # "self" should not be included in constructor arguments - self.assertNotIn( - 'Foo(self', - example_file - ) - self.assertFalse( - os.path.exists('_build/text/autoapi/method_multiline') - ) - index_path = '_build/text/index.txt' - with io.open(index_path, encoding='utf8') as index_handle: - index_file = index_handle.read() - self.assertIn( - 'Sphinx AutoAPI Index', - index_file - ) - self.assertIn( - 'Foo', - index_file - ) - self.assertIn( - 'Meta', - index_file - ) - - def test_integration_with_package(self): - with sphinx_build('pypackageexample'): - example_path = '_build/text/autoapi/example/index.txt' - with io.open(example_path, encoding='utf8') as example_handle: - example_file = example_handle.read() - self.assertIn( - 'example.foo', - example_file - ) - self.assertIn( - 'example.module_level_method(foo, bar)', - example_file - ) - - example_foo_path = '_build/text/autoapi/example/foo/index.txt' - with io.open(example_foo_path, encoding='utf8') as example_foo_handle: - example_foo_file = example_foo_handle.read() - self.assertIn( - 'class example.foo.Foo', - example_foo_file - ) - self.assertIn( - 'method_okay(self, foo=None, bar=None)', - example_foo_file - ) - - index_path = '_build/text/index.txt' - with io.open(index_path, encoding='utf8') as index_handle: - index_file = index_handle.read() - self.assertIn( - 'Sphinx AutoAPI Index', - index_file - ) - self.assertIn( - 'example.foo', - index_file - ) - self.assertIn( - 'Foo', - index_file - ) - self.assertIn( - 'module_level_method', - index_file - ) - - @pytest.mark.skipif(sphinx.version_info < (1, 4), - reason="Cannot override extensions in Sphinx 1.3") - def test_napoleon_integration(self): - with sphinx_build('pyexample'): - example_path = '_build/text/autoapi/example/index.txt' - with io.open(example_path, encoding='utf8') as example_handle: - example_file = example_handle.read() - - # Check that docstrings are not transformed without napoleon loaded - self.assertIn( - 'Args', - example_file - ) - - self.assertIn( - 'Returns', - example_file - ) - - confoverrides={ - 'extensions': [ - 'autoapi.extension', - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - ], - } - - with sphinx_build('pyexample', confoverrides=confoverrides): - example_path = '_build/text/autoapi/example/index.txt' - with io.open(example_path, encoding='utf8') as example_handle: - example_file = example_handle.read() - - self.assertIn( - 'Parameters', - example_file - ) - - self.assertIn( - 'Return type', - example_file - ) - - self.assertNotIn( - 'Args', - example_file - ) - - def _test_class_content(self, class_content): - confoverrides={ - 'autoapi_python_class_content': class_content, - } - - with sphinx_build('pyexample', confoverrides=confoverrides): - example_path = '_build/text/autoapi/example/index.txt' - with io.open(example_path, encoding='utf8') as example_handle: - example_file = example_handle.read() - - assert_class = self.assertIn - if class_content == 'init': - assert_class = self.assertNotIn - - assert_class( - 'Can we parse arguments', - example_file - ) - - assert_init = self.assertIn - if class_content not in ('both', 'init'): - assert_init = self.assertNotIn - - assert_init( - 'Constructor docstring', - example_file - ) - - def test_class_class_content(self): - self._test_class_content('class') - - def test_both_class_content(self): - self._test_class_content('both') - - def test_init_class_content(self): - self._test_class_content('init') - - def test_hiding_private_members(self): - confoverrides = { - 'autoapi_options': ['members', 'undoc-members', 'special-members'], - } - with sphinx_build('pypackageexample', confoverrides=confoverrides): - example_path = '_build/text/autoapi/example/index.txt' - with io.open(example_path, encoding='utf8') as example_handle: - example_file = example_handle.read() - - self.assertNotIn('private', example_file) - - private_path = '_build/text/autoapi/example/_private_module/index.txt' - with io.open(private_path, encoding='utf8') as private_handle: - private_file = private_handle.read() - - self.assertIn('public_method', private_file) - - class DotNetTests(LanguageIntegrationTests): def _dotnet_read(self, path):