twisted / pydoctor

This is pydoctor, an API documentation generator that works by static analysis.
https://pydoctor.readthedocs.io
Other
190 stars 51 forks source link

"tox -p all" to noise for extern contributors #756

Closed buhtz closed 10 months ago

buhtz commented 11 months ago

The contributors docu mention the use of tox -p all on the contributors local machine before opening a PR. The output is nearly useless because it is to noisy. So much work into unit tests and tox...

Make it short and clear. It is waste of time, ressources, energey/CO2 if users are not able to understand or even read that output.

A site problem is that it is unclear how to log the whole output to a text file. Does tox have an option for that? Piping stderr and stdout on shell should not be an option for regular users.

I really care for pydoctor. That is why I am so stuborn. :smiley: I am also member of a maintenance team of a damn old Python GUI application. We are the third generation. The code smells a lot is nearly untested and untestable and also nothing is documented. But we took the challenge to clear up things, writing documentation and attract more contributors to the project.

Output like this does not help contributors and not you. The effect is the opposite: Driving contributors away and wasting resources (and nerves) of the maintainer and burning him out on the long run. I try to keep you healthy. :laughing: :heart:

$ tox -p all
/home/user/.local/lib/python3.9/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!

        ********************************************************************************
        Please avoid running ``setup.py`` directly.
        Instead, use pypa/build, pypa/installer or other
        standards-based tools.

        See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.
        ********************************************************************************

!!
  self.initialize_options()
pyflakes: OK ✔ in 8.57 seconds
mypy: FAIL ✖ in 16.83 seconds
mypy: install_package> python -I -m pip install --force-reinstall --no-deps /home/user/ownCloud/my.work/pydoctor/.tox/.tmp/package/17/pydoctor-23.9.0.dev0.tar.gz
mypy: commands[0]> mypy --cache-dir=/home/user/ownCloud/my.work/pydoctor/.tox/mypy_cache --exclude=pydoctor/test/testpackages --pretty pydoctor docs/epytext_demo
pydoctor/epydoc/markup/restructuredtext.py:50: error: Unused "type: ignore" comment, use narrower [import-untyped] instead of [import] code  [unused-ignore]
    from docutils.parsers.rst.directives.admonitions import BaseAdmonition # type: ignore[import]
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pydoctor/templatewriter/__init__.py:20: error: Cannot find implementation or library stub for module named "importlib.resources.abc"  [import-not-found]
            from importlib.resources.abc import Traversable
    ^
pydoctor/templatewriter/__init__.py:20: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
Found 2 errors in 2 files (checked 87 source files)
mypy: exit 1 (10.44 seconds) /home/user/ownCloud/my.work/pydoctor> mypy --cache-dir=/home/user/ownCloud/my.work/pydoctor/.tox/mypy_cache --exclude=pydoctor/test/testpackages --pretty pydoctor docs/epytext_demo pid=12621
testdocs: FAIL ✖ in 20.46 seconds
testdocs: install_package> python -I -m pip install --force-reinstall --no-deps /home/user/ownCloud/my.work/pydoctor/.tox/.tmp/package/16/pydoctor-23.9.0.dev0.tar.gz
testdocs: commands[0]> echo '::group::Sphinx build'
::group::Sphinx build
testdocs: commands[1]> sphinx-build -aE -b html -W /home/user/ownCloud/my.work/pydoctor/docs/source /home/user/ownCloud/my.work/pydoctor/build/docs
Sphinx v7.2.6 in Verwendung
Initializing Spelling Checker 8.0.0
Building 'main' pydoctor API docs as:
--make-html
--quiet
--html-output=/home/user/ownCloud/my.work/pydoctor/build/docs/api/
--project-name=pydoctor
--project-version=23.9.0.dev0
--docformat=epytext
--privacy=HIDDEN:pydoctor.test
--project-url=../index.html
/home/user/ownCloud/my.work/pydoctor/pydoctor
--html-viewsource-base=https://github.com/twisted/pydoctor/tree/fix/720hardlink_index
--project-base-dir=/home/user/ownCloud/my.work/pydoctor
--config=/home/user/ownCloud/my.work/pydoctor/setup.cfg
testdocs: exit 2 (13.93 seconds) /home/user/ownCloud/my.work/pydoctor> sphinx-build -aE -b html -W /home/user/ownCloud/my.work/pydoctor/docs/source /home/user/ownCloud/my.work/pydoctor/build/docs pid=12632
.pkg: _exit> python /home/user/.local/lib/python3.9/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
test: install_package> python -I -m pip install --force-reinstall --no-deps /home/user/ownCloud/my.work/pydoctor/.tox/.tmp/package/15/pydoctor-23.9.0.dev0.tar.gz
test: commands[0]> coverage erase
test: commands[1]> coverage run -m pytest -vv pydoctor
=============================================================================================== test session starts ===============================================================================================
platform linux -- Python 3.9.2, pytest-7.4.3, pluggy-1.3.0 -- /home/user/ownCloud/my.work/pydoctor/.tox/test/bin/python
cachedir: .tox/test/.pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/home/user/ownCloud/my.work/pydoctor/.hypothesis/examples'))
rootdir: /home/user/ownCloud/my.work/pydoctor
configfile: setup.cfg
plugins: hypothesis-6.92.2, subtests-0.11.0
collected 1331 items                                                                                                                                                                                              

pydoctor/test/test_astbuilder.py::test_node2fullname PASSED                                                                                                                                                 [  0%]
pydoctor/test/test_astbuilder.py::test_no_docstring[System] PASSED                                                                                                                                          [  0%]
pydoctor/test/test_astbuilder.py::test_no_docstring[ZopeInterfaceSystem] PASSED                                                                                                                             [  0%]

[...SNIPPED...]

pydoctor/test/test_napoleon_iterators.py::ModifyIterTest::test_modifier_rstrip_unicode PASSED                                                                                                               [ 77%]
pydoctor/test/test_node2stan.py::test_gettext PASSED                                                                                                                                                        [ 78%]
pydoctor/test/test_node2stan.py::test_docutils_get_lineno_title_reference PASSED                                                                                                                            [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[pydoctor]\nintersphinx = ["https://docs.python.org/3/objects.inv",\n                "https://twistedmatrix.com/documents/current/api/objects.inv",\n                "https://urllib3.readthedocs.io/en/latest/objects.inv",\n                "https://requests.readthedocs.io/en/latest/objects.inv",\n                "https://www.attrs.org/en/stable/objects.inv",\n                "https://tristanlatr.github.io/apidocs/docutils/objects.inv"]\ndocformat = 'restructuredtext'\nproject-name = 'MyProject'\nproject-url = "https://github.com/twisted/pydoctor"\nprivacy = ["HIDDEN:pydoctor.test"]\nquiet = 1\nwarnings-as-errors = true\n-\n[tool.poetry]\npackages = [\n    { include = "my_package" },\n    { include = "extra_package" },\n]\nname = "awesome"\n\n[tool.poetry.dependencies]\n# These packages are mandatory and form the core of this package\u2019s distribution.\nmandatory = "^1.0"\n\n# A list of all of the optional dependencies, some of which are included in the\n# below `extras`. They can be opted into by apps.\npsycopg2 = { version = "^2.7", optional = true }\nmysqlclient = { version = "^1.3", optional = true }\n\n[tool.poetry.extras]\nmysql = ["mysqlclient"]\npgsql = ["psycopg2"]\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[pydoctor]\nintersphinx = ["https://docs.python.org/3/objects.inv",\n                "https://twistedmatrix.com/documents/current/api/objects.inv",\n                "https://urllib3.readthedocs.io/en/latest/objects.inv",\n                "https://requests.readthedocs.io/en/latest/objects.inv",\n                "https://www.attrs.org/en/stable/objects.inv",\n                "https://tristanlatr.github.io/apidocs/docutils/objects.inv"]\ndocformat = 'restructuredtext'\nproject-name = 'MyProject'\nproject-url = "https://github.com/twisted/pydoctor"\nprivacy = ["HIDDEN:pydoctor.test"]\nquiet = 1\nwarnings-as-errors = true\n-\n[metadata]\nname = setup.cfg\nversion = 0.9.0.dev\nauthor = Erik M. Bray\nauthor-email = embray@stsci.edu\nsummary = Reads a distributions's metadata from its setup.cfg file and passes it to setuptools.setup()\ndescription-file =\n    README.rst\n    CHANGES.rst\nhome-page = http://pypi.python.org/pypi/setup.cfg\nrequires-dist = setuptools\nclassifier =\n    Development Status :: 5 - Production/Stable\n    Environment :: Plugins\n    Framework :: Setuptools Plugin\n    Intended Audience :: Developers\n    License :: OSI Approved :: BSD License\n    Operating System :: OS Independent\n    Programming Language :: Python\n    Programming Language :: Python :: 3\n    Topic :: Software Development :: Build Tools\n    Topic :: Software Development :: Libraries :: Python Modules\n    Topic :: System :: Archiving :: Packaging\n\n[files]\npackages =\n    setup\n    setup.cfg\n    setup.cfg.extern\nextra_files =\n    CHANGES.rst\n    LICENSE\n    ez_setup.py\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[tool.pydoctor]\nintersphinx = ["https://docs.python.org/3/objects.inv",\n                "https://twistedmatrix.com/documents/current/api/objects.inv",\n                "https://urllib3.readthedocs.io/en/latest/objects.inv",\n                "https://requests.readthedocs.io/en/latest/objects.inv",\n                "https://www.attrs.org/en/stable/objects.inv",\n                "https://tristanlatr.github.io/apidocs/docutils/objects.inv"]\ndocformat = "restructuredtext"\nproject-name = "MyProject"\nproject-url = "https://github.com/twisted/pydoctor"\nprivacy = ["HIDDEN:pydoctor.test"]\nquiet = 1\nwarnings-as-errors = true\n-\n[tool.poetry]\npackages = [\n    { include = "my_package" },\n    { include = "extra_package" },\n]\nname = "awesome"\n\n[tool.poetry.dependencies]\n# These packages are mandatory and form the core of this package\u2019s distribution.\nmandatory = "^1.0"\n\n# A list of all of the optional dependencies, some of which are included in the\n# below `extras`. They can be opted into by apps.\npsycopg2 = { version = "^2.7", optional = true }\nmysqlclient = { version = "^1.3", optional = true }\n\n[tool.poetry.extras]\nmysql = ["mysqlclient"]\npgsql = ["psycopg2"]\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[tool.pydoctor]\nintersphinx = ["https://docs.python.org/3/objects.inv",\n                "https://twistedmatrix.com/documents/current/api/objects.inv",\n                "https://urllib3.readthedocs.io/en/latest/objects.inv",\n                "https://requests.readthedocs.io/en/latest/objects.inv",\n                "https://www.attrs.org/en/stable/objects.inv",\n                "https://tristanlatr.github.io/apidocs/docutils/objects.inv"]\ndocformat = "restructuredtext"\nproject-name = "MyProject"\nproject-url = "https://github.com/twisted/pydoctor"\nprivacy = ["HIDDEN:pydoctor.test"]\nquiet = 1\nwarnings-as-errors = true\n-\n[metadata]\nname = setup.cfg\nversion = 0.9.0.dev\nauthor = Erik M. Bray\nauthor-email = embray@stsci.edu\nsummary = Reads a distributions's metadata from its setup.cfg file and passes it to setuptools.setup()\ndescription-file =\n    README.rst\n    CHANGES.rst\nhome-page = http://pypi.python.org/pypi/setup.cfg\nrequires-dist = setuptools\nclassifier =\n    Development Status :: 5 - Production/Stable\n    Environment :: Plugins\n    Framework :: Setuptools Plugin\n    Intended Audience :: Developers\n    License :: OSI Approved :: BSD License\n    Operating System :: OS Independent\n    Programming Language :: Python\n    Programming Language :: Python :: 3\n    Topic :: Software Development :: Build Tools\n    Topic :: Software Development :: Libraries :: Python Modules\n    Topic :: System :: Archiving :: Packaging\n\n[files]\npackages =\n    setup\n    setup.cfg\n    setup.cfg.extern\nextra_files =\n    CHANGES.rst\n    LICENSE\n    ez_setup.py\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[tool:pydoctor]\nintersphinx = \n    https://docs.python.org/3/objects.inv\n    https://twistedmatrix.com/documents/current/api/objects.inv\n    https://urllib3.readthedocs.io/en/latest/objects.inv\n    https://requests.readthedocs.io/en/latest/objects.inv\n    https://www.attrs.org/en/stable/objects.inv\n    https://tristanlatr.github.io/apidocs/docutils/objects.inv\ndocformat = restructuredtext\nproject-name = MyProject\nproject-url = https://github.com/twisted/pydoctor\nprivacy = \n    HIDDEN:pydoctor.test\nquiet = 1\nwarnings-as-errors = true\n-\n[tool.poetry]\npackages = [\n    { include = "my_package" },\n    { include = "extra_package" },\n]\nname = "awesome"\n\n[tool.poetry.dependencies]\n# These packages are mandatory and form the core of this package\u2019s distribution.\nmandatory = "^1.0"\n\n# A list of all of the optional dependencies, some of which are included in the\n# below `extras`. They can be opted into by apps.\npsycopg2 = { version = "^2.7", optional = true }\nmysqlclient = { version = "^1.3", optional = true }\n\n[tool.poetry.extras]\nmysql = ["mysqlclient"]\npgsql = ["psycopg2"]\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[tool:pydoctor]\nintersphinx = \n    https://docs.python.org/3/objects.inv\n    https://twistedmatrix.com/documents/current/api/objects.inv\n    https://urllib3.readthedocs.io/en/latest/objects.inv\n    https://requests.readthedocs.io/en/latest/objects.inv\n    https://www.attrs.org/en/stable/objects.inv\n    https://tristanlatr.github.io/apidocs/docutils/objects.inv\ndocformat = restructuredtext\nproject-name = MyProject\nproject-url = https://github.com/twisted/pydoctor\nprivacy = \n    HIDDEN:pydoctor.test\nquiet = 1\nwarnings-as-errors = true\n-\n[metadata]\nname = setup.cfg\nversion = 0.9.0.dev\nauthor = Erik M. Bray\nauthor-email = embray@stsci.edu\nsummary = Reads a distributions's metadata from its setup.cfg file and passes it to setuptools.setup()\ndescription-file =\n    README.rst\n    CHANGES.rst\nhome-page = http://pypi.python.org/pypi/setup.cfg\nrequires-dist = setuptools\nclassifier =\n    Development Status :: 5 - Production/Stable\n    Environment :: Plugins\n    Framework :: Setuptools Plugin\n    Intended Audience :: Developers\n    License :: OSI Approved :: BSD License\n    Operating System :: OS Independent\n    Programming Language :: Python\n    Programming Language :: Python :: 3\n    Topic :: Software Development :: Build Tools\n    Topic :: Software Development :: Libraries :: Python Modules\n    Topic :: System :: Archiving :: Packaging\n\n[files]\npackages =\n    setup\n    setup.cfg\n    setup.cfg.extern\nextra_files =\n    CHANGES.rst\n    LICENSE\n    ez_setup.py\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[pydoctor]\nintersphinx: ["https://docs.python.org/3/objects.inv",\n                "https://twistedmatrix.com/documents/current/api/objects.inv",\n                "https://urllib3.readthedocs.io/en/latest/objects.inv",\n                "https://requests.readthedocs.io/en/latest/objects.inv",\n                "https://www.attrs.org/en/stable/objects.inv",\n                "https://tristanlatr.github.io/apidocs/docutils/objects.inv"]\ndocformat: restructuredtext\nproject-name: MyProject\nproject-url: '''https://github.com/twisted/pydoctor'''\nprivacy = \n    HIDDEN:pydoctor.test\nquiet = 1\nwarnings-as-errors = true\n-\n[tool.poetry]\npackages = [\n    { include = "my_package" },\n    { include = "extra_package" },\n]\nname = "awesome"\n\n[tool.poetry.dependencies]\n# These packages are mandatory and form the core of this package\u2019s distribution.\nmandatory = "^1.0"\n\n# A list of all of the optional dependencies, some of which are included in the\n# below `extras`. They can be opted into by apps.\npsycopg2 = { version = "^2.7", optional = true }\nmysqlclient = { version = "^1.3", optional = true }\n\n[tool.poetry.extras]\nmysql = ["mysqlclient"]\npgsql = ["psycopg2"]\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_config_parsers[\n[pydoctor]\nintersphinx: ["https://docs.python.org/3/objects.inv",\n                "https://twistedmatrix.com/documents/current/api/objects.inv",\n                "https://urllib3.readthedocs.io/en/latest/objects.inv",\n                "https://requests.readthedocs.io/en/latest/objects.inv",\n                "https://www.attrs.org/en/stable/objects.inv",\n                "https://tristanlatr.github.io/apidocs/docutils/objects.inv"]\ndocformat: restructuredtext\nproject-name: MyProject\nproject-url: '''https://github.com/twisted/pydoctor'''\nprivacy = \n    HIDDEN:pydoctor.test\nquiet = 1\nwarnings-as-errors = true\n-\n[metadata]\nname = setup.cfg\nversion = 0.9.0.dev\nauthor = Erik M. Bray\nauthor-email = embray@stsci.edu\nsummary = Reads a distributions's metadata from its setup.cfg file and passes it to setuptools.setup()\ndescription-file =\n    README.rst\n    CHANGES.rst\nhome-page = http://pypi.python.org/pypi/setup.cfg\nrequires-dist = setuptools\nclassifier =\n    Development Status :: 5 - Production/Stable\n    Environment :: Plugins\n    Framework :: Setuptools Plugin\n    Intended Audience :: Developers\n    License :: OSI Approved :: BSD License\n    Operating System :: OS Independent\n    Programming Language :: Python\n    Programming Language :: Python :: 3\n    Topic :: Software Development :: Build Tools\n    Topic :: Software Development :: Libraries :: Python Modules\n    Topic :: System :: Archiving :: Packaging\n\n[files]\npackages =\n    setup\n    setup.cfg\n    setup.cfg.extern\nextra_files =\n    CHANGES.rst\n    LICENSE\n    ez_setup.py\n] PASSED [ 78%]
pydoctor/test/test_options.py::test_repeatable_options_multiple_configs_and_args PASSED                                                                                                                     [ 78%]
pydoctor/test/test_options.py::test_validations PASSED                                                                                                                                                      [ 78%]
pydoctor/test/test_packages.py::test_relative_import PASSED                                                                                                                                                 [ 78%]

[...SNIPPED...]

pydoctor/test/epydoc/test_restructuredtext.py::test_summary[numpy] PASSED                                                                                                                                   [ 99%]
pydoctor/test/epydoc/test_restructuredtext.py::test_summary[google] PASSED                                                                                                                                  [ 99%]
pydoctor/test/epydoc/test_restructuredtext.py::test_get_toc PASSED                                                                                                                                          [100%]

==================================================================================================== FAILURES =====================================================================================================
____________________________________________________________________________________________ test_main_symlinked_paths ____________________________________________________________________________________________

tmp_path = PosixPath('/tmp/pytest-of-user/pytest-2/test_main_symlinked_paths0')

    def test_main_symlinked_paths(tmp_path: Path) -> None:
        """
        The project base directory and package/module directories are normalized
        in the same way, such that System.setSourceHref() can call Path.relative_to()
        on them.
        """
        link = tmp_path / 'src'
        link.symlink_to(Path.cwd(), target_is_directory=True)

        print(f'{link=} {link.exists()=}')
        print(f'{Path.cwd()=} {Path.cwd().exists()=}')

>       exit_code = driver.main(args=[
            '--project-base-dir=.',
            '--html-viewsource-base=http://example.com',
            f'{link}/pydoctor/test/testpackages/basic/'
            ])

pydoctor/test/test_commandline.py:221: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
pydoctor/driver.py:169: in main
    make(system)
pydoctor/driver.py:128: in make
    writer.writeSummaryPages(system)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pydoctor.templatewriter.writer.TemplateWriter object at 0x7f508fe1c0a0>, system = <pydoctor.model.System object at 0x7f5090d11a90>

    def writeSummaryPages(self, system: model.System) -> None:
        import time
        for pclass in itertools.chain(summary.summaryPages(system), search.searchpages):
            system.msg('html', 'starting ' + pclass.__name__ + ' ...', nonl=True)
            T = time.time()
            page = pclass(system=system, template_lookup=self.template_lookup)
            with self.build_directory.joinpath(pclass.filename).open('wb') as fobj:
                flattenToFile(fobj, page)
            system.msg('html', "took %fs"%(time.time() - T), wantsnl=False)

        # Generate the searchindex.json file
        system.msg('html', 'starting lunr search index ...', nonl=True)
        T = time.time()
        search.write_lunr_index(self.build_directory, system=system)
        system.msg('html', "took %fs"%(time.time() - T), wantsnl=False)

        if len(system.root_names) == 1:
            # If there is just a single root module it is written to index.html to produce nicer URLs.
            # To not break old links we also create a symlink from the full module name to the index.html
            # file. This is also good for consistency: every module is accessible by <full module name>.html
            root_module_path = (self.build_directory / (list(system.root_names)[0] + '.html'))
            try:
                root_module_path.unlink()
                # not using missing_ok=True because that was only added in Python 3.8 and we still support Python 3.6
            except FileNotFoundError:
                pass

            # When support for Python 3.9 and older is dropped use
            # pathlib.Path.hardlink_to() instead.
            x = list(root_module_path.parent.glob('*'))
            print(f'TTTT\n{root_module_path=} {x=}')
            print(f'{root_module_path.exists()=}')
>           os.link(
                src=root_module_path,  # original
                dst=root_module_path.parent / 'index.html'  # the hardlink
            )
E           FileNotFoundError: [Errno 2] No such file or directory: 'apidocs/basic.html' -> 'apidocs/index.html'

pydoctor/templatewriter/writer.py:118: FileNotFoundError
---------------------------------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------------------------------
link=PosixPath('/tmp/pytest-of-user/pytest-2/test_main_symlinked_paths0/src') link.exists()=True
Path.cwd()=PosixPath('/home/user/ownCloud/my.work/pydoctor') Path.cwd().exists()=True
adding directory /home/user/ownCloud/my.work/pydoctor/pydoctor/test/testpackages/basic
Guessing 'basic' for project name.
writing html to apidocs using pydoctor.templatewriter.writer.TemplateWriter
starting ModuleIndexPage ...took 0.008509s
starting ClassIndexPage ...took 0.007703s
starting NameIndexPage ...took 0.020201s
starting UndocumentedSummaryPage ...took 0.008427s
starting AllDocuments ...took 0.028705s
starting lunr search index ...took 0.017618s
TTTT
root_module_path=PosixPath('apidocs/basic.html') x=[PosixPath('apidocs/undoccedSummary.html'), PosixPath('apidocs/searchlib.js'), PosixPath('apidocs/fullsearchindex.json'), PosixPath('apidocs/bootstrap.min.css'), PosixPath('apidocs/apidocs.css'), PosixPath('apidocs/extra.css'), PosixPath('apidocs/.gitattributes'), PosixPath('apidocs/searchindex.json'), PosixPath('apidocs/fonts'), PosixPath('apidocs/all-documents.html'), PosixPath('apidocs/ajax.js'), PosixPath('apidocs/nameIndex.html'), PosixPath('apidocs/pydoctor.js'), PosixPath('apidocs/classIndex.html'), PosixPath('apidocs/moduleIndex.html'), PosixPath('apidocs/search.js'), PosixPath('apidocs/lunr.js'), PosixPath('apidocs/sidebartoggle.js')]
root_module_path.exists()=False
________________________________________________________________________________________ test_main_source_outside_basedir _________________________________________________________________________________________

capsys = <_pytest.capture.CaptureFixture object at 0x7f508fcb24c0>

    def test_main_source_outside_basedir(capsys: CapSys) -> None:
        """
        If a --project-base-dir is given, all package and module paths should
        be located inside that base directory if source links wants to be generated.
        Otherwise it's OK, but no source links will be genrated
        """
>       assert driver.main(args=[
            '--html-viewsource-base=notnone',
            '--project-base-dir=docs',
            'pydoctor/test/testpackages/basic/'
            ]) == 0

pydoctor/test/test_commandline.py:235: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
pydoctor/driver.py:169: in main
    make(system)
pydoctor/driver.py:128: in make
    writer.writeSummaryPages(system)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pydoctor.templatewriter.writer.TemplateWriter object at 0x7f508fab7250>, system = <pydoctor.model.System object at 0x7f508fab7d60>

    def writeSummaryPages(self, system: model.System) -> None:
        import time
        for pclass in itertools.chain(summary.summaryPages(system), search.searchpages):
            system.msg('html', 'starting ' + pclass.__name__ + ' ...', nonl=True)
            T = time.time()
            page = pclass(system=system, template_lookup=self.template_lookup)
            with self.build_directory.joinpath(pclass.filename).open('wb') as fobj:
                flattenToFile(fobj, page)
            system.msg('html', "took %fs"%(time.time() - T), wantsnl=False)

        # Generate the searchindex.json file
        system.msg('html', 'starting lunr search index ...', nonl=True)
        T = time.time()
        search.write_lunr_index(self.build_directory, system=system)
        system.msg('html', "took %fs"%(time.time() - T), wantsnl=False)

        if len(system.root_names) == 1:
            # If there is just a single root module it is written to index.html to produce nicer URLs.
            # To not break old links we also create a symlink from the full module name to the index.html
            # file. This is also good for consistency: every module is accessible by <full module name>.html
            root_module_path = (self.build_directory / (list(system.root_names)[0] + '.html'))
            try:
                root_module_path.unlink()
                # not using missing_ok=True because that was only added in Python 3.8 and we still support Python 3.6
            except FileNotFoundError:
                pass

            # When support for Python 3.9 and older is dropped use
            # pathlib.Path.hardlink_to() instead.
            x = list(root_module_path.parent.glob('*'))
            print(f'TTTT\n{root_module_path=} {x=}')
            print(f'{root_module_path.exists()=}')
>           os.link(
                src=root_module_path,  # original
                dst=root_module_path.parent / 'index.html'  # the hardlink
            )
E           FileNotFoundError: [Errno 2] No such file or directory: 'apidocs/basic.html' -> 'apidocs/index.html'

pydoctor/templatewriter/writer.py:118: FileNotFoundError
---------------------------------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------------------------------
No source links can be generated for module /home/user/ownCloud/my.work/pydoctor/pydoctor/test/testpackages/basic: source path lies outside base directory /home/user/ownCloud/my.work/pydoctor/docs
adding directory /home/user/ownCloud/my.work/pydoctor/pydoctor/test/testpackages/basic
Guessing 'basic' for project name.
writing html to apidocs using pydoctor.templatewriter.writer.TemplateWriter
starting ModuleIndexPage ...took 0.009070s
starting ClassIndexPage ...took 0.010268s
starting NameIndexPage ...took 0.030738s
starting UndocumentedSummaryPage ...took 0.007238s
starting AllDocuments ...took 0.027273s
starting lunr search index ...took 0.015048s
TTTT
root_module_path=PosixPath('apidocs/basic.html') x=[PosixPath('apidocs/undoccedSummary.html'), PosixPath('apidocs/searchlib.js'), PosixPath('apidocs/fullsearchindex.json'), PosixPath('apidocs/bootstrap.min.css'), PosixPath('apidocs/apidocs.css'), PosixPath('apidocs/extra.css'), PosixPath('apidocs/.gitattributes'), PosixPath('apidocs/searchindex.json'), PosixPath('apidocs/fonts'), PosixPath('apidocs/all-documents.html'), PosixPath('apidocs/ajax.js'), PosixPath('apidocs/nameIndex.html'), PosixPath('apidocs/pydoctor.js'), PosixPath('apidocs/classIndex.html'), PosixPath('apidocs/moduleIndex.html'), PosixPath('apidocs/search.js'), PosixPath('apidocs/lunr.js'), PosixPath('apidocs/sidebartoggle.js')]
root_module_path.exists()=False
_______________________________________________________________________________________________ test_basic_package ________________________________________________________________________________________________

tmp_path = PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0')

    def test_basic_package(tmp_path: Path) -> None:
        system = processPackage("basic")
        w = writer.TemplateWriter(tmp_path, TemplateLookup(template_dir))
        w.prepOutputDirectory()
        root, = system.rootobjects
        w._writeDocsFor(root)
>       w.writeSummaryPages(system)

pydoctor/test/test_templatewriter.py:141: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pydoctor.templatewriter.writer.TemplateWriter object at 0x7f508c6dcc70>, system = <pydoctor.model.System object at 0x7f5091cccd60>

    def writeSummaryPages(self, system: model.System) -> None:
        import time
        for pclass in itertools.chain(summary.summaryPages(system), search.searchpages):
            system.msg('html', 'starting ' + pclass.__name__ + ' ...', nonl=True)
            T = time.time()
            page = pclass(system=system, template_lookup=self.template_lookup)
            with self.build_directory.joinpath(pclass.filename).open('wb') as fobj:
                flattenToFile(fobj, page)
            system.msg('html', "took %fs"%(time.time() - T), wantsnl=False)

        # Generate the searchindex.json file
        system.msg('html', 'starting lunr search index ...', nonl=True)
        T = time.time()
        search.write_lunr_index(self.build_directory, system=system)
        system.msg('html', "took %fs"%(time.time() - T), wantsnl=False)

        if len(system.root_names) == 1:
            # If there is just a single root module it is written to index.html to produce nicer URLs.
            # To not break old links we also create a symlink from the full module name to the index.html
            # file. This is also good for consistency: every module is accessible by <full module name>.html
            root_module_path = (self.build_directory / (list(system.root_names)[0] + '.html'))
            try:
                root_module_path.unlink()
                # not using missing_ok=True because that was only added in Python 3.8 and we still support Python 3.6
            except FileNotFoundError:
                pass

            # When support for Python 3.9 and older is dropped use
            # pathlib.Path.hardlink_to() instead.
            x = list(root_module_path.parent.glob('*'))
            print(f'TTTT\n{root_module_path=} {x=}')
            print(f'{root_module_path.exists()=}')
>           os.link(
                src=root_module_path,  # original
                dst=root_module_path.parent / 'index.html'  # the hardlink
            )
E           FileNotFoundError: [Errno 2] No such file or directory: '/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.html' -> '/tmp/pytest-of-user/pytest-2/test_basic_package0/index.html'

pydoctor/templatewriter/writer.py:118: FileNotFoundError
---------------------------------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------------------------------
adding directory /home/user/ownCloud/my.work/pydoctor/pydoctor/test/testpackages/basic
processing ['basic']
processing ['basic._private_mod']
processing ['basic.mod']
Package 'basic'
Module 'basic._private_mod'
Module 'basic.mod'
Class 'basic.mod.C'
Class 'basic.mod.C.S'
Class 'basic.mod.D'
Class 'basic.mod.D.T'
starting ModuleIndexPage ...took 0.005082s
starting ClassIndexPage ...took 0.004911s
starting NameIndexPage ...took 0.014976s
starting UndocumentedSummaryPage ...took 0.005869s
starting AllDocuments ...took 0.014865s
starting lunr search index ...took 0.011266s
TTTT
root_module_path=PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.html') x=[PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/fullsearchindex.json'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/searchindex.json'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/all-documents.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/undoccedSummary.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/nameIndex.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/classIndex.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/moduleIndex.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.mod.D.T.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.mod.D.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.mod.C.S.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.mod.C.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.mod.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/basic._private_mod.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/index.html'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/sidebartoggle.js'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/lunr.js'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/search.js'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/pydoctor.js'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/ajax.js'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/fonts'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/extra.css'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/apidocs.css'), PosixPath('/tmp/pytest-of-user/pytest-2/test_basic_package0/searchlib.js')]
root_module_path.exists()=False
============================================================================================= short test summary info =============================================================================================
FAILED pydoctor/test/test_commandline.py::test_main_symlinked_paths - FileNotFoundError: [Errno 2] No such file or directory: 'apidocs/basic.html' -> 'apidocs/index.html'
FAILED pydoctor/test/test_commandline.py::test_main_source_outside_basedir - FileNotFoundError: [Errno 2] No such file or directory: 'apidocs/basic.html' -> 'apidocs/index.html'
FAILED pydoctor/test/test_templatewriter.py::test_basic_package - FileNotFoundError: [Errno 2] No such file or directory: '/tmp/pytest-of-user/pytest-2/test_basic_package0/basic.html' -> '/tmp/pytest-of-user/pytest-2/test_basic_package0/index.html'
========================================================================= 3 failed, 1326 passed, 2 xfailed, 43 subtests passed in 38.17s ==========================================================================
test: exit 1 (39.10 seconds) /home/user/ownCloud/my.work/pydoctor> coverage run -m pytest -vv pydoctor pid=12642
  test: FAIL code 1 (45.93=setup[6.62]+cmd[0.21,39.10] seconds)
  pyflakes: OK (8.57=setup[6.21]+cmd[2.02,0.34] seconds)
  mypy: FAIL code 1 (16.83=setup[6.39]+cmd[10.44] seconds)
  testdocs: FAIL code 2 (20.46=setup[6.52]+cmd[0.01,13.93] seconds)
  evaluation failed :( (46.21 seconds)

Extension error (pydoctor.sphinx_ext.build_apidocs):
Handler <function on_builder_inited at 0x7f3c92ba10d0> for event 'builder-inited' threw an exception (exception: [Errno 2] Datei oder Verzeichnis nicht gefunden: '/home/user/ownCloud/my.work/pydoctor/build/docs/api/pydoctor.html' -> '/home/user/ownCloud/my.work/pydoctor/build/docs/api/index.html')
tristanlatr commented 11 months ago

I don’t know what to say honestly. I’m really glad you care about pydoctor. But you have to stop being so judgmental. Let’s focus on actionable items, here your not suggesting anything.

Does tox have an option for that? Piping stderr and stdout on shell should not be an option for regular users.

I assume that tox have an option for that. And sorry to put it this way but developers should be able to handle simple stdout redirects. We are not regular users: I simply cannot vulgarize everything contributors needs to know. But I agree things could be streamlined a little bit.

Now what are the action points in your opinion?

tristanlatr commented 11 months ago

The simple thing to do would be to ask the developers to only run tox -e test locally and defer the rest of the tests to the ci. Then we briefly explain each ci step in our contribution docs.

Then we move all dependencies listing int the Setup.cfg file and eventually flush the tox requirements for external contributors. But that’s going to work only for the unit tests, other tests requires tox.

buhtz commented 11 months ago

My main problem is that tox is used. But as I stated before this is up to the maintainer to decide it. I am not enough into tox or into your project to build an objective opinion about the question if tox is really needed and useful here beside that it is just a fancy tool.

From my point of view as an extern contributor and the tasks I have to do, tox do not help. The tools and dependencies to use for an extern person should be as less as possible to keep the hurdle low. Contributors do not need a special thanks or applause but FOSS maintainers should be happy about every contribution they could get even if they are worse. The latter, handled a correct way, could be transformed into a healthy and productive relationship.

In short: IMHO maintainers should do a bit advertising and marketing when it comes to contributors to attract them, not driving them away and keeping them connected to the project.

So if you do not have a strong indication I would recommend to remove tox from the processes where extern contributors are involved. If you can not do this for good reasons you need to document it. The latter is because tox is not a regular and usual tool. Not everyone knows how to handle it. And contributors should not take the burden to read manuals of tools they won't use in their own workflows. Even "pytest" is not regular in that context. The unitest module is regular. Everything else is an extra and might increase the burden of contributors. That you use pytest-style tests should be part of the documentation. And that test code need to be documented better (as I stated in another issue) because pytest-tests are very hard to read because they hide to much and doing things implicit.

Tox and most of your CI jobs should not be removed but hidden from contributors. E.g. the primer thing could be done when you prepare a release. In my own project we do have a prepare-release-checklist where we run and activate stronger tests. It could be done/triggered in a manual way and do not need to be in a CI job.

Two options:

  1. Remove/hide tox.
  2. Document the use of tox for extern contributors and do it like you explain it to a 6 year old.

About redirecting on bash: Bash is for inter-machine/-process communication and not invented for humans. The machine need to attach to the human not the other way around. There are exceptions of course; using Emacs with evil-mode is a compromise (like in a marriage) between human and the machine. Redirecting stderr/stdout is a stupid job the machine should do and hide from myself. That is why I ask for something like a log option for tox.

tristanlatr commented 11 months ago

Tox is not a hard requirement for external users (see my last comment on #753), I agree it’s not clear in our documentation. But contributors can figure that out by reading the setup.cfg file. Though pytest will still be required.

The primer cannot be only run when preparing a release because it looses the point: which to catch regressions the fastest as possible. So that will be run on every PR.

I believe this is all a matter of documenting our tests infrastructure. So let me a week to draft a better contributors documentation. I’ll ask your review then.