Repository: sphinx-contrib/spelling (language: python)

Issue:
7.6.0: pytest is faioing in few units
I'm packaging your module as an rpm package so I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.
- `python3 -sBm build -w --no-isolation`
- because I'm calling `build` with `--no-isolation` I'm using during all processes only locally installed modules
- install .whl file in </install/prefix>
- run pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>

Here is pytest output:
<details>

```console
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-sphinxcontrib-spelling-7.6.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-sphinxcontrib-spelling-7.6.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/spelling-7.6.0
collected 62 items

tests/test_builder.py ...F......F....FFFF                                                                                                                            [ 30%]
tests/test_checker.py FFFF..                                                                                                                                         [ 40%]
tests/test_filter.py ....sssssssssssssssssssssssssss......                                                                                                           [100%]

================================================================================= FAILURES =================================================================================
___________________________________________________________________________ test_ignore_literals ___________________________________________________________________________

sphinx_project = (local('/tmp/pytest-of-tkloczko/pytest-918/test_ignore_literals0/src'), local('/tmp/pytest-of-tkloczko/pytest-918/test_ignore_literals0/out'))

    def test_ignore_literals(sphinx_project):
        srcdir, outdir = sphinx_project
        add_file(srcdir, 'contents.rst', '''
        Welcome to Spelling Checker documentation!
        ==========================================

        There are several misspelled words in this text.

        ::

            Literal blocks are ignoreed.

        Inline ``litterals`` are ignored, too.

        ''')
        stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, 'contents')
        # The 'contents.spelling' output file should not have been
        # created, because the errors are ignored.
>       assert output_text is None
E       AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_ignore_literals0/src/contents.rst:3: (Welcome)  Welcome to Spel..../../../../../tmp/pytest-of-tkloczko/pytest-918/test_ignore_literals0/src/contents.rst:11: (too)   are ignored, too.\n' is None

tests/test_builder.py:135: AssertionError
--------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
fatal: not a git repository (or any parent up to mount point /home/tkloczko)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
---------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
WARNING  test.filters:filters.py:249 Called: git log --quiet --no-color --pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn
WARNING  test.filters:filters.py:250 Failed to scan contributors: Command '['git', 'log', '--quiet', '--no-color', '--pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn']' returned non-zero exit status 128.
_____________________________________________________________________________ test_docstrings ______________________________________________________________________________

sphinx_project = (local('/tmp/pytest-of-tkloczko/pytest-918/test_docstrings0/src'), local('/tmp/pytest-of-tkloczko/pytest-918/test_docstrings0/out'))

    def test_docstrings(sphinx_project):
        srcdir, outdir = sphinx_project

        add_file(srcdir, 'conf.py', '''
        extensions = ['sphinxcontrib.spelling', 'sphinx.ext.autodoc']
        ''')

        add_file(srcdir / '..', 'the_source.py', '''
        #!/usr/bin/env python3

        def public_function(arg_name):
            """Does something useful.

            :param arg_name: Pass a vaule
            """
            return 1
        ''')

        add_file(srcdir, 'contents.rst', '''
        The Module
        ==========

        .. automodule:: the_source
           :members:

        ''')

        with working_dir(srcdir / '..'):
            with import_path(['.'] + sys.path):
                stdout, stderr, output_text = get_sphinx_output(
                    srcdir,
                    outdir,
                    'contents',
                )

        # Expected string is too long for one line
        expected = (
            'the_source.py:'
            'docstring of the_source.public_function:'
            'None:'
            ' (vaule)'
        )
>       assert expected in output_text
E       AssertionError: assert 'the_source.py:docstring of the_source.public_function:None: (vaule)' in 'src/contents.rst:3: (The)  The Module\nsrc/contents.rst:3: (Module)  The Module\nthe_source.py:docstring of the_sourc...public_function:1: (a)  Pass a vaule\nthe_source.py:docstring of the_source.public_function:1: (vaule)  Pass a vaule\n'

tests/test_builder.py:296: AssertionError
--------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
---------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
WARNING  test.filters:filters.py:249 Called: git log --quiet --no-color --pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn
WARNING  test.filters:filters.py:250 Failed to scan contributors: Command '['git', 'log', '--quiet', '--no-color', '--pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn']' returned non-zero exit status 128.
__________________________________________________________________________ test_legacy_directive ___________________________________________________________________________

sphinx_project = (local('/tmp/pytest-of-tkloczko/pytest-918/test_legacy_directive0/src'), local('/tmp/pytest-of-tkloczko/pytest-918/test_legacy_directive0/out'))

    def test_legacy_directive(sphinx_project):
        srcdir, outdir = sphinx_project

        add_file(srcdir, 'contents.rst', '''
        The Module
        ==========

        .. spelling::

           teh

        teh is OK

        ''')

        stdout, stderr, output_text = get_sphinx_output(
            srcdir,
            outdir,
            'contents',
        )
>       assert output_text is None
E       AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_legacy_directive0/src/contents.rst:3: (The)  The Module\n../../... Module\n../../../../../tmp/pytest-of-tkloczko/pytest-918/test_legacy_directive0/src/contents.rst:9: (is)  teh is OK\n' is None

tests/test_builder.py:376: AssertionError
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
contents ['teh']
--------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
fatal: not a git repository (or any parent up to mount point /home/tkloczko)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
---------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
WARNING  test.filters:filters.py:249 Called: git log --quiet --no-color --pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn
WARNING  test.filters:filters.py:250 Failed to scan contributors: Command '['git', 'log', '--quiet', '--no-color', '--pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn']' returned non-zero exit status 128.
__________________________________________________________________________ test_domain_directive ___________________________________________________________________________

sphinx_project = (local('/tmp/pytest-of-tkloczko/pytest-918/test_domain_directive0/src'), local('/tmp/pytest-of-tkloczko/pytest-918/test_domain_directive0/out'))

    def test_domain_directive(sphinx_project):
        srcdir, outdir = sphinx_project

        add_file(srcdir, 'contents.rst', '''
        The Module
        ==========

        .. spelling:word-list::

           teh

        teh is OK

        ''')

        stdout, stderr, output_text = get_sphinx_output(
            srcdir,
            outdir,
            'contents',
        )
>       assert output_text is None
E       AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_directive0/src/contents.rst:3: (The)  The Module\n../../... Module\n../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_directive0/src/contents.rst:9: (is)  teh is OK\n' is None

tests/test_builder.py:399: AssertionError
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
contents ['teh']
--------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
fatal: not a git repository (or any parent up to mount point /home/tkloczko)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
---------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
WARNING  test.filters:filters.py:249 Called: git log --quiet --no-color --pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn
WARNING  test.filters:filters.py:250 Failed to scan contributors: Command '['git', 'log', '--quiet', '--no-color', '--pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn']' returned non-zero exit status 128.
_____________________________________________________________________________ test_domain_role _____________________________________________________________________________

sphinx_project = (local('/tmp/pytest-of-tkloczko/pytest-918/test_domain_role0/src'), local('/tmp/pytest-of-tkloczko/pytest-918/test_domain_role0/out'))

    def test_domain_role(sphinx_project):
        srcdir, outdir = sphinx_project

        add_file(srcdir, 'contents.rst', '''
        The Module
        ==========

        :spelling:word:`teh` is OK

        ''')

        stdout, stderr, output_text = get_sphinx_output(
            srcdir,
            outdir,
            'contents',
        )
>       assert output_text is None
E       AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_role0/src/contents.rst:3: (The)  The Module\n../../../.....ling:word:`teh`\n../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_role0/src/contents.rst:5: (is)   is OK\n' is None

tests/test_builder.py:418: AssertionError
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
contents ['teh']
--------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
fatal: not a git repository (or any parent up to mount point /home/tkloczko)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
---------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
WARNING  test.filters:filters.py:249 Called: git log --quiet --no-color --pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn
WARNING  test.filters:filters.py:250 Failed to scan contributors: Command '['git', 'log', '--quiet', '--no-color', '--pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn']' returned non-zero exit status 128.
_____________________________________________________________________ test_domain_role_multiple_words ______________________________________________________________________

sphinx_project = (local('/tmp/pytest-of-tkloczko/pytest-918/test_domain_role_multiple_word0/src'), local('/tmp/pytest-of-tkloczko/pytest-918/test_domain_role_multiple_word0/out'))

    def test_domain_role_multiple_words(sphinx_project):
        srcdir, outdir = sphinx_project

        add_file(srcdir, 'contents.rst', '''
        The Module
        ==========

        :spelling:word:`teh is KO`

        ''')

        stdout, stderr, output_text = get_sphinx_output(
            srcdir,
            outdir,
            'contents',
        )
>       assert output_text is None
E       AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_role_multiple_word0/src/contents.rst:3: (The)  The Modul...pytest-of-tkloczko/pytest-918/test_domain_role_multiple_word0/src/contents.rst:5: (word)  :spelling:word:`teh is KO`\n' is None

tests/test_builder.py:437: AssertionError
--------------------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------------------
contents ['teh', 'is', 'KO']
--------------------------------------------------------------------------- Captured stderr call ---------------------------------------------------------------------------
fatal: not a git repository (or any parent up to mount point /home/tkloczko)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
---------------------------------------------------------------------------- Captured log call -----------------------------------------------------------------------------
WARNING  test.filters:filters.py:249 Called: git log --quiet --no-color --pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn
WARNING  test.filters:filters.py:250 Failed to scan contributors: Command '['git', 'log', '--quiet', '--no-color', '--pretty=format:%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn']' returned non-zero exit status 128.
_____________________________________________________________________________ test_errors_only _____________________________________________________________________________

    def test_errors_only():
        checker = SpellingChecker(lang='en_US',
                                  suggest=False,
                                  word_list_filename=None,
                                  )
        for word, suggestions, line, offset in checker.check('This txt is wrong'):
            assert not suggestions, 'Suggesting'
>           assert word == 'txt'
E           AssertionError: assert 'This' == 'txt'
E             - txt
E             + This

tests/test_checker.py:19: AssertionError
__________________________________________________________________________ test_with_suggestions ___________________________________________________________________________

    def test_with_suggestions():
        checker = SpellingChecker(lang='en_US',
                                  suggest=True,
                                  word_list_filename=None,
                                  )
        for word, suggestions, line, offset in checker.check('This txt is wrong'):
>           assert suggestions, 'Not suggesting'
E           AssertionError: Not suggesting
E           assert []

tests/test_checker.py:30: AssertionError
____________________________________________________________________________ test_with_wordlist ____________________________________________________________________________

    def test_with_wordlist():
        checker = SpellingChecker(
            lang='en_US',
            suggest=False,
            word_list_filename=os.path.join(os.path.dirname(__file__),
                                            'test_wordlist.txt')
        )
        words = [w for w, s, l, o in checker.check('This txt is wrong')]
>       assert not words, 'Did not use personal word list file'
E       AssertionError: Did not use personal word list file
E       assert not ['This', 'is', 'wrong']

tests/test_checker.py:43: AssertionError
__________________________________________________________________________ test_with_context_line __________________________________________________________________________

    def test_with_context_line():
        checker = SpellingChecker(lang='en_US',
                                  suggest=False,
                                  word_list_filename=None,
                                  context_line=True,
                                  )

        text = 'Line one\nThis txt is wrong\nLine two'
        for word, suggestions, line, offset in checker.check(text):
            assert not suggestions, 'Suggesting'
>           assert word == 'txt'
E           AssertionError: assert 'Line' == 'txt'
E             - txt
E             + Line

tests/test_checker.py:56: AssertionError
============================================================================= warnings summary =============================================================================
tests/test_builder.py: 10 warnings
  /home/tkloczko/rpmbuild/BUILD/spelling-7.6.0/sphinxcontrib/spelling/builder.py:217: PendingDeprecationWarning: nodes.Node.traverse() is obsoleted by Node.findall().
    for node in doctree.traverse(docutils.nodes.Text):

tests/test_builder.py::test_domain_role
tests/test_builder.py::test_domain_role_multiple_words
  /usr/lib/python3.8/site-packages/docutils/parsers/rst/roles.py:205: DeprecationWarning: nodes.Text: initialization argument "rawsource" is ignored and will be removed in Docutils 1.3.
    return [self.node_class(rawtext, text, **options)], []

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================================= short test summary info ==========================================================================
SKIPPED [27] tests/test_filter.py:49: Not a git repo
FAILED tests/test_builder.py::test_ignore_literals - AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_ignore_literals0/src/contents.rst:3: (...
FAILED tests/test_builder.py::test_docstrings - AssertionError: assert 'the_source.py:docstring of the_source.public_function:None: (vaule)' in 'src/contents.rst:3: (The...
FAILED tests/test_builder.py::test_legacy_directive - AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_legacy_directive0/src/contents.rst:3:...
FAILED tests/test_builder.py::test_domain_directive - AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_directive0/src/contents.rst:3:...
FAILED tests/test_builder.py::test_domain_role - AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_role0/src/contents.rst:3: (The)  Th...
FAILED tests/test_builder.py::test_domain_role_multiple_words - AssertionError: assert '../../../../../tmp/pytest-of-tkloczko/pytest-918/test_domain_role_multiple_word0/...
FAILED tests/test_checker.py::test_errors_only - AssertionError: assert 'This' == 'txt'
FAILED tests/test_checker.py::test_with_suggestions - AssertionError: Not suggesting
FAILED tests/test_checker.py::test_with_wordlist - AssertionError: Did not use personal word list file
FAILED tests/test_checker.py::test_with_context_line - AssertionError: assert 'Line' == 'txt'
========================================================== 10 failed, 25 passed, 27 skipped, 12 warnings in 1.42s ==========================================================
```
</details>

Here is list of installed modules in build env
<details>

```console
Package                       Version
----------------------------- -----------------
alabaster                     0.7.12
attrs                         22.1.0
Babel                         2.10.2
Brlapi                        0.8.3
build                         0.8.0
charset-normalizer            2.1.0
codespell                     2.1.0
cycler                        0.11.0
distro                        1.7.0
docutils                      0.18.1
extras                        1.0.0
fixtures                      4.0.0
fonttools                     4.37.1
gpg                           1.17.1-unknown
idna                          3.3
imagesize                     1.4.1
importlib-metadata            4.12.0
iniconfig                     1.1.1
Jinja2                        3.1.1
kiwisolver                    1.4.4
libcomps                      0.1.18
louis                         3.22.0
MarkupSafe                    2.1.1
matplotlib                    3.5.3
numpy                         1.23.1
olefile                       0.46
packaging                     21.3
pbr                           5.9.0
pep517                        0.12.0
Pillow                        9.2.0
pip                           22.2.1
pluggy                        1.0.0
py                            1.11.0
pyenchant                     3.2.2
Pygments                      2.13.0
PyGObject                     3.42.2
pyparsing                     3.0.9
pytest                        7.1.2
python-dateutil               2.8.2
pytz                          2022.1
requests                      2.28.1
rpm                           4.17.0
setuptools                    65.3.0
six                           1.16.0
snowballstemmer               2.2.0
Sphinx                        5.1.1
sphinxcontrib-applehelp       1.0.2.dev20220730
sphinxcontrib-devhelp         1.0.2.dev20220730
sphinxcontrib-htmlhelp        2.0.0
sphinxcontrib-jsmath          1.0.1.dev20220730
sphinxcontrib-qthelp          1.0.3.dev20220730
sphinxcontrib-serializinghtml 1.1.5
sphinxcontrib-spelling        7.5.0
testtools                     2.5.0
tomli                         2.0.1
urllib3                       1.26.12
wheel                         0.37.1
zipp                          3.8.1
```
</details>



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index bfdff71..54195c9 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -16,6 +16,7 @@ jobs:
           - linter
           - pkglint
           - spelling
+          - isolated
 
     steps:
       - uses: actions/checkout@v2
diff --git a/.mergify.yml b/.mergify.yml
index 7f7d49f..6bebd7e 100644
--- a/.mergify.yml
+++ b/.mergify.yml
@@ -25,6 +25,7 @@ pull_request_rules:
     conditions:
       - and:
         - "check-success=build (docs)"
+        - "check-success=build (isolated)"
         - "check-success=build (linter)"
         - "check-success=build (spelling)"
         - "check-success=django"
diff --git a/tox.ini b/tox.ini
index 24d6d00..e0bc073 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,6 +19,20 @@ passenv=
     # the shell environment before running tox.
     PYENCHANT_LIBRARY_PATH
 
+[testenv:isolated]
+isolated_build=true
+changedir={envtmpdir}
+allowlist_externals =
+    /bin/cp
+    /bin/rm
+commands=
+    rm -rf tests
+    cp -a {toxinidir}/tests .
+    python -m pytest \
+      --cov=sphinxcontrib.spelling \
+      --cov-report term-missing \
+      --log-level DEBUG
+
 [testenv:style]
 deps =
     isort>=5.0.1


Test patch:
diff --git a/tests/helpers.py b/tests/helpers.py
new file mode 100644
index 0000000..e4cf463
--- /dev/null
+++ b/tests/helpers.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import os
+import pathlib
+
+import pytest
+
+
+def require_git_repo(f):
+    return pytest.mark.skipif(
+        not (pathlib.Path(os.getcwd()) / '.git').is_dir(),
+        reason='Not a git repo')(f)
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 29a3a2a..9ed7800 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -10,8 +10,11 @@
 import textwrap
 
 import pytest
+import sphinx
 from sphinx.application import Sphinx
 
+from tests import helpers # isort:skip
+
 
 def _make_sphinx_project(tmpdir):
     srcdir = tmpdir.mkdir('src')
@@ -251,6 +254,7 @@ def test_ignore_file(sphinx_project):
     assert output_text is None
 
 
+@helpers.require_git_repo
 def test_docstrings(sphinx_project):
     srcdir, outdir = sphinx_project
 
@@ -286,11 +290,17 @@ def public_function(arg_name):
                 'contents',
             )
 
+    # Sphinx 5.1.0 and later reports line numbers for docstring
+    # content.
+    line_num = 'None:'
+    if sphinx.version_info[:3] >= (5, 1, 0):
+        line_num = '1:'
+
     # Expected string is too long for one line
     expected = (
         'the_source.py:'
         'docstring of the_source.public_function:'
-        'None:'
+    ) + line_num + (
         ' (vaule)'
     )
     assert expected in output_text
diff --git a/tests/test_filter.py b/tests/test_filter.py
index bf76e64..3601f85 100644
--- a/tests/test_filter.py
+++ b/tests/test_filter.py
@@ -8,12 +8,12 @@
 import logging
 import os
 import sys
-from pathlib import Path
 
 import pytest
 from enchant.tokenize import get_tokenizer
 
 from sphinxcontrib.spelling import filters # isort:skip
+from tests import helpers # isort:skip
 
 # Replace the sphinx logger with a normal one so pytest can collect
 # the output.
@@ -46,7 +46,7 @@ def test_acronym_unicode():
     assert 'DBM' not in words, 'Failed to filter out acronym'
 
 
-@pytest.mark.skipif(not (Path(os.getcwd()) / '.git').is_dir(), reason='Not a git repo')
+@helpers.require_git_repo
 @pytest.mark.parametrize(
     "name",
     [