Repository: sphinx-contrib/spelling

Issue:
No output.txt file, only getting a tree of *.spelling files
I'm running the command `spinx-build -b spelling -d _build/doctree . _build/spelling` and I get a tree of files showing the misspellings per file, but no output.txt file with all the misspelled words. The docs seem to indicate I should get such a file. 

Any idea why not?

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 88f937c..4044e32 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,8 @@ matrix:
   include:
     - env: BUILD=docs
       python: 3.7
+    - env: BUILD=spelling
+      python: 3.7
     - env: BUILD=linter
       python: 3.8
     - env: BUILD=pkglint
@@ -39,7 +41,7 @@ script:
   # Conditional builds (related to env passed)
   - |
     case ${BUILD} in
-        docs|pep8|cover|linter)
+        docs|pep8|cover|linter|spelling)
             tox -e ${BUILD}
             ;;
         *)
diff --git a/docs/source/run.rst b/docs/source/run.rst
index 2cb1521..e660bf9 100644
--- a/docs/source/run.rst
+++ b/docs/source/run.rst
@@ -6,34 +6,63 @@ To process a document with the spell checker, use ``sphinx-build`` and
 specify ``spelling`` as the builder name using the ``-b`` option.  The
 output includes the headings from the document and any misspelled
 words.  If suggestions are enabled, they are shown on the same line as
-the misspelling. A log of all the words not found in the dictionary is
-saved to the file ``spelling/output.txt`` under the build directory.
+the misspelling. A log of the words in each input file not found in
+the dictionary is saved to the file ``<input>.spelling`` under the
+build directory.
 
-::
+.. code-block:: console
 
-    $ make spelling
-    sphinx-build -W -b spelling -d build/doctrees   source build/spelling
-    Running Sphinx v1.0.7
-    Initializing Spelling Checker
-    loading pickled environment... done
-    building [spelling]: all documents
-    updating environment: 1 added, 2 changed, 0 removed
-    reading sources... [ 33%] history
-    reading sources... [ 66%] index
-    reading sources... [100%] run
+   $ tox -e spelling -r
+   spelling create: .../sphinxcontrib-spelling/.tox/spelling
+   spelling installdeps: .[docs]
+   spelling develop-inst: .../sphinxcontrib-spelling
+   spelling installed: -f /Users/dhellmann/.pip/wheelhouse,alabaster==0.7.12,Babel==2.8.0,certifi==2020.6.20,chardet==3.0.4,docutils==0.16,dulwich==0.20.5,idna==2.10,imagesize==1.2.0,importlib-metadata==1.7.0,Jinja2==2.11.2,MarkupSafe==1.1.1,packaging==20.4,pbr==5.4.5,pyenchant==3.1.1,Pygments==2.6.1,pyparsing==2.4.7,pytz==2020.1,PyYAML==5.3.1,reno==3.1.0,requests==2.24.0,six==1.15.0,snowballstemmer==2.0.0,Sphinx==3.2.0,sphinxcontrib-applehelp==1.0.2,sphinxcontrib-devhelp==1.0.2,sphinxcontrib-htmlhelp==1.0.3,sphinxcontrib-jsmath==1.0.1,sphinxcontrib-qthelp==1.0.3,sphinxcontrib-serializinghtml==1.1.4,-e git+git@github.com:sphinx-contrib/spelling.git@b0b3e2a8c935701cfcbbc76ea1aa501a03ae4e22#egg=sphinxcontrib_spelling,urllib3==1.25.10,zipp==3.1.0
+   spelling run-test-pre: PYTHONHASHSEED='1632297322'
+   spelling run-test: commands[0] | sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling
+   Running Sphinx v3.2.0
+   Initializing Spelling Checker 5.2.1.dev2
+   Ignoring wiki words
+   Ignoring acronyms
+   Adding package names from PyPI to local dictionary…
+   Ignoring Python builtins
+   Ignoring importable module names
+   Adding contents of .../sphinxcontrib-spelling/docs/source/spelling_wordlist.txt to custom word list
+   Adding contents of .../sphinxcontrib-spelling/docs/source/spelling_people.txt to custom word list
+   Looking for custom word list in /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmphsetrn0s/spelling_wordlist.txt
+   building [mo]: targets for 0 po files that are out of date
+   building [spelling]: all documents
+   updating environment: [new config] 6 added, 0 changed, 0 removed
+   reading sources... [ 16%] customize
+   reading sources... [ 33%] developers
+   reading sources... [ 50%] history
+   reading sources... [ 66%] index
+   reading sources... [ 83%] install
+   reading sources... [100%] run
 
-    looking for now-outdated files... none found
-    pickling environment... done
-    checking consistency... done
-    preparing documents... done
-    writing output... [ 20%] developers
-    writing output... [ 40%] history
-    writing output... [ 60%] index
-    writing output... [ 80%] install
-    (line  28) PyEnchant ["Penchant"]
-    writing output... [100%] run
+   waiting for workers...
+   scanning .../sphinxcontrib-spelling/releasenotes/notes for current branch release notes
+   got versions ['5.2.0']
+   looking for now-outdated files... none found
+   pickling environment... done
+   checking consistency... done
+   preparing documents... done
+   writing output... [ 16%] customize
+   Extending local dictionary for customize
+   writing output... [ 33%] developers
+   Extending local dictionary for developers
+   writing output... [ 50%] history
+   Extending local dictionary for history
+   writing output... [ 66%] index
+   Extending local dictionary for index
+   index.rst:17:speel:["Peel", "peel", "spell", "spiel", "Speer", "speed", "steel", "sepal", "spill", "spoil", "spool", "speller", "Pele", "supple", "Perl", "spew", "spree", "suppl", "repel", "spells", "spiels", "spleen", "peal", "seal", "seep", "sell", "Aspell", "Ispell", "sleep", "spell's", "spiel's"]:I can't speel.
+   Writing .../sphinxcontrib-spelling/docs/build/spelling/index.spelling
+   writing output... [ 83%] install
+   Extending local dictionary for install
+   writing output... [100%] run
 
-    Spelling checker messages written to ./docs/build/spelling/output.txt
-    build finished with problems.
-    make: *** [spelling] Error 1
 
+   Warning, treated as error:
+   Found 1 misspelled words
+   ERROR: InvocationError for command .../sphinxcontrib-spelling/.tox/spelling/bin/sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling (exited with code 2)
+   __________________________________________________ summary ___________________________________________________
+   ERROR:   spelling: commands failed
diff --git a/sphinxcontrib/spelling/builder.py b/sphinxcontrib/spelling/builder.py
index 9b9f25e..4702318 100644
--- a/sphinxcontrib/spelling/builder.py
+++ b/sphinxcontrib/spelling/builder.py
@@ -166,12 +166,16 @@ class SpellingBuilder(Builder):
     ])
 
     def write_doc(self, docname, doctree):
-        output_filename = os.path.join(self.outdir, docname + '.spelling')
-        ensuredir(os.path.dirname(output_filename))
-        with io.open(output_filename, 'w', encoding='UTF-8') as output:
-            self._do_write(output, docname, doctree)
+        lines = list(self._find_misspellings(docname, doctree))
+        self.misspelling_count += len(lines)
+        if lines:
+            output_filename = os.path.join(self.outdir, docname + '.spelling')
+            logger.info('Writing %s', output_filename)
+            ensuredir(os.path.dirname(output_filename))
+            with io.open(output_filename, 'w', encoding='UTF-8') as output:
+                output.writelines(lines)
 
-    def _do_write(self, output, docname, doctree):
+    def _find_misspellings(self, docname, doctree):
 
         # Build the document-specific word filter based on any good
         # words listed in spelling directives. If we have no such
@@ -214,13 +218,12 @@ class SpellingBuilder(Builder):
                     msg_parts.append(context_line)
                     msg = ':'.join(msg_parts)
                     logger.info(msg)
-                    output.write(u"%s:%s: (%s) %s %s\n" % (
+                    yield u"%s:%s: (%s) %s %s\n" % (
                         self.env.doc2path(docname, None),
                         lineno, word,
                         self.format_suggestions(suggestions),
                         context_line,
-                    ))
-                    self.misspelling_count += 1
+                    )
 
         self.checker.pop_filters()
         return
diff --git a/tox.ini b/tox.ini
index 832baeb..34a5388 100644
--- a/tox.ini
+++ b/tox.ini
@@ -46,3 +46,13 @@ deps =
 commands =
     sphinx-build -W -j auto -b html -d docs/build/doctrees docs/source docs/build/html
     sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling
+
+[testenv:spelling]
+basepython=python3.7
+setenv =
+    BUILD=docs
+    ENABLE_SPELLING=1
+deps =
+    .[docs]
+commands =
+    sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling


Test patch:
diff --git a/sphinxcontrib/spelling/tests/test_builder.py b/sphinxcontrib/spelling/tests/test_builder.py
index b7032f5..976e483 100644
--- a/sphinxcontrib/spelling/tests/test_builder.py
+++ b/sphinxcontrib/spelling/tests/test_builder.py
@@ -40,8 +40,11 @@ def get_sphinx_output(srcdir, outdir, docname):
     )
     app.build()
     path = os.path.join(outdir, docname + '.spelling')
-    with codecs.open(path, 'r') as f:
-        output_text = f.read()
+    try:
+        with codecs.open(path, 'r') as f:
+            output_text = f.read()
+    except FileNotFoundError:
+        output_text = None
     return (stdout, stderr, output_text)
 
 
@@ -98,8 +101,9 @@ def test_ignore_literals(sphinx_project):
 
     ''')
     stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, 'contents')
-    assert '(ignoreed)' not in output_text
-    assert '(litterals)' not in output_text
+    # The 'contents.spelling' output file should not have been
+    # created, because the errors are ignored.
+    assert output_text is None
 
 
 def test_several_word_lists(sphinx_project):