Skip to content

Commit

Permalink
Merge pull request #59 from dhellmann/parallel-build
Browse files Browse the repository at this point in the history
support parallel builds
  • Loading branch information
dhellmann authored Jul 28, 2020
2 parents 8e9a156 + 17c8401 commit a98b54b
Show file tree
Hide file tree
Showing 21 changed files with 164 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ matrix:
- python: "nightly"
include:
- env: BUILD=docs
python: 3.8
python: 3.7
- env: BUILD=linter
python: 3.8
- env: BUILD=pkglint
Expand Down
6 changes: 1 addition & 5 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,10 @@

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
#
# CANNOT ENABLE SPHINXCONTRIB.SPELLING because ReadTheDocs.org does not support
# PyEnchant.
extensions = [
'reno.sphinxext',
'sphinxcontrib.spelling',
]
if os.getenv('ENABLE_SPELLING'):
extensions.append('sphinxcontrib.spelling')

spelling_word_list_filename = [
'spelling_wordlist.txt',
Expand Down
4 changes: 2 additions & 2 deletions docs/source/customize.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. .. spelling::
.. spelling::

.. wikis
wikis

=======================
Configuration Options
Expand Down
6 changes: 3 additions & 3 deletions docs/source/developers.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.. .. spelling::
.. spelling::

.. sphinxcontrib
.. reStructuredText
sphinxcontrib
reStructuredText

============
Developers
Expand Down
4 changes: 4 additions & 0 deletions docs/source/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
Release History
=================

.. spelling::

unmaintained

.. release-notes::

5.1.2
Expand Down
4 changes: 2 additions & 2 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. .. spelling::
.. spelling::

.. sphinxcontrib
sphinxcontrib

.. sphinxcontrib.spelling documentation master file, created by
sphinx-quickstart on Sun Apr 17 15:33:23 2011.
Expand Down
20 changes: 9 additions & 11 deletions docs/source/install.rst
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
.. .. spelling::
.. spelling::

wikis
sphinxcontrib

==============
Installation
==============

Installing sphinxcontrib.spelling
=================================
1. Install the extension with pip: ``pip install sphinxcontrib-spelling``

Install the extension with pip: ``pip install sphinxcontrib-spelling``
2. Add ``'sphinxcontrib.spelling'`` to the ``extensions`` list in
``conf.py``.

Configuration
=============
.. code-block:: python
1. Add ``'sphinxcontrib.spelling'`` to the ``extensions`` list in ``conf.py``.
extensions = [ 'sphinxcontrib.spelling' ]
::
3. Then pass ``"spelling"`` as the builder argument to ``sphinx-build``.

extensions = [ 'sphinxcontrib.spelling' ]
.. code-block:: shell-session
.. _install-options:
$ sphinx-build -b spelling docs/source docs/build
1 change: 1 addition & 0 deletions docs/source/spelling_people.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Finucane
Gaynor
Gonsiorowski
Hong
Hong
Huon
Kampik
Kolosov
Expand Down
2 changes: 2 additions & 0 deletions docs/source/spelling_wordlist.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
builtins
hoc
linter
linters
pypi
reStructuredText
sphinxcontrib
Expand Down
8 changes: 8 additions & 0 deletions releasenotes/notes/entry-point-b2ad290cd46d055c.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
features:
- |
The builder is now registered using an entry point, so that if the
``spelling`` directive is not used in a project
``sphinxcontrib.spelling`` does not need to be included explicitly
in the ``extensions`` list in ``conf.py`` in order to use it with
the project on the command line.
8 changes: 8 additions & 0 deletions releasenotes/notes/optional-pyenchant-76a049d1d6104cfb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
features:
- |
PyEnchant is an optional dependency. If it is not installer, the
spell checker will not work, but the extension can still be
initialized. This allows projects that use spell checking to
publish their documentation to ``readthedocs.org``, where it is
not possible to install PyEnchant.
7 changes: 7 additions & 0 deletions releasenotes/notes/parallel-builds-ea64713dd82a4498.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
Restore support for parallel builds. Words that do not appear in
any configured dictionary are written to a file named based on the
input file, with the ``.rst`` extension replaced with
``.spelling``.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
PyEnchant>=3.1.1
Sphinx>=3.0.0
importlib_metadata>=1.7.0;python_version<'3.8'
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ linter =
docs =
reno

[entry_points]
sphinx.builders =
spelling = sphinxcontrib.spelling

[egg_info]
#tag_build = dev
#tag_date = true
Expand Down
20 changes: 18 additions & 2 deletions sphinxcontrib/spelling/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import inspect

try:
# For python 3.8 and later
import importlib.metadata as importlib_metadata
except ImportError:
# For everyone else
import importlib_metadata

from sphinx.util import logging

from .asset import SpellingCollector
from .builder import SpellingBuilder
from .directive import SpellingDirective

logger = logging.getLogger(__name__)


def setup(app):
version = importlib_metadata.version('sphinxcontrib-spelling')
# If we are running inside the test suite, "app" will be a module.
if inspect.ismodule(app):
return
logger.info('Initializing Spelling Checker')
logger.info('Initializing Spelling Checker %s', version)
app.add_builder(SpellingBuilder)
# Register the 'spelling' directive for setting parameters within
# a document
app.add_directive('spelling', SpellingDirective)
# Register an environment collector to merge data gathered by the
# directive in parallel builds
app.add_env_collector(SpellingCollector)
# Report guesses about correct spelling
app.add_config_value('spelling_show_suggestions', False, 'env')
# Report the whole line that has the error
Expand All @@ -43,4 +55,8 @@ def setup(app):
app.add_config_value('spelling_ignore_importable_modules', True, 'env')
# Add any user-defined filter classes
app.add_config_value('spelling_filters', [], 'env')
return {"parallel_read_safe": False}
return {
"parallel_read_safe": True,
"parallel_write_safe": True,
"version": version,
}
33 changes: 33 additions & 0 deletions sphinxcontrib/spelling/asset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# encoding: utf-8
#
# Copyright (c) 2020 Doug Hellmann. All rights reserved.
#
"""Asset collector for additional spelling terms."""

import collections
import contextlib

from sphinx.environment.collectors import EnvironmentCollector
from sphinx.util import logging

logger = logging.getLogger(__name__)


class SpellingCollector(EnvironmentCollector):

def clear_doc(self, app, env, docname) -> None:
with contextlib.suppress(AttributeError, KeyError):
del env.spelling_document_words[docname]

def merge_other(self, app, env, docnames, other):
try:
other_words = other.spelling_document_words
except AttributeError:
other_words = {}

if not hasattr(env, 'spelling_document_words'):
env.spelling_document_words = collections.defaultdict(list)
env.spelling_document_words.update(other_words)

def process_doc(self, app, doctree):
pass
43 changes: 32 additions & 11 deletions sphinxcontrib/spelling/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@
from sphinx.builders import Builder
from sphinx.util import logging
from sphinx.util.console import darkgreen, red
from sphinx.util.osutil import ensuredir

from enchant.tokenize import EmailFilter, WikiWordFilter
try:
from enchant.tokenize import EmailFilter, WikiWordFilter
except ImportError as imp_exc:
enchant_import_error = imp_exc
else:
enchant_import_error = None

from . import checker
from . import filters
Expand All @@ -33,14 +39,18 @@ class SpellingBuilder(Builder):
name = 'spelling'

def init(self):
if enchant_import_error is not None:
raise RuntimeError(
'Cannot initialize spelling builder '
'without PyEnchant installed') from enchant_import_error
self.docnames = []
self.document_data = []
self.misspelling_count = 0

self.env.settings["smart_quotes"] = False
# Initialize the per-document filters
if not hasattr(self.env, 'spelling_document_filters'):
self.env.spelling_document_filters = collections.defaultdict(list)
if not hasattr(self.env, 'spelling_document_words'):
self.env.spelling_document_words = collections.defaultdict(list)

# Initialize the global filters
f = [
Expand Down Expand Up @@ -79,9 +89,6 @@ def init(self):
context_line=self.config.spelling_show_whole_line,
)

self.output_filename = os.path.join(self.outdir, 'output.txt')
self.output = io.open(self.output_filename, 'w', encoding='UTF-8')

def _load_filter_classes(self, filters):
# Filters may be expressed in the configuration file using
# names, so look through them and import the referenced class
Expand Down Expand Up @@ -159,7 +166,24 @@ def format_suggestions(self, suggestions):
])

def write_doc(self, docname, doctree):
self.checker.push_filters(self.env.spelling_document_filters[docname])
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)

def _do_write(self, output, docname, doctree):

# Build the document-specific word filter based on any good
# words listed in spelling directives. If we have no such
# words, we want to push an empty list of filters so that we
# can always safely pop the filter stack when we are done with
# this document.
doc_filters = []
good_words = self.env.spelling_document_words.get(docname)
if good_words:
logger.info('Extending local dictionary for %s', docname)
doc_filters.append(filters.IgnoreWordsFilterFactory(good_words))
self.checker.push_filters(doc_filters)

for node in doctree.traverse(docutils.nodes.Text):
if (node.tagname == '#text' and
Expand Down Expand Up @@ -190,7 +214,7 @@ def write_doc(self, docname, doctree):
msg_parts.append(context_line)
msg = ':'.join(msg_parts)
logger.info(msg)
self.output.write(u"%s:%s: (%s) %s %s\n" % (
output.write(u"%s:%s: (%s) %s %s\n" % (
self.env.doc2path(docname, None),
lineno, word,
self.format_suggestions(suggestions),
Expand All @@ -202,9 +226,6 @@ def write_doc(self, docname, doctree):
return

def finish(self):
self.output.close()
logger.info('Spelling checker messages written to %s' %
self.output_filename)
if self.misspelling_count:
logger.warning('Found %d misspelled words' %
self.misspelling_count)
Expand Down
14 changes: 12 additions & 2 deletions sphinxcontrib/spelling/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
"""Spelling checker extension for Sphinx.
"""

import enchant
from enchant.tokenize import get_tokenizer
try:
import enchant
from enchant.tokenize import get_tokenizer
except ImportError as imp_exc:
enchant_import_error = imp_exc
else:
enchant_import_error = None


class SpellingChecker(object):
Expand All @@ -18,6 +23,11 @@ class SpellingChecker(object):

def __init__(self, lang, suggest, word_list_filename,
tokenizer_lang='en_US', filters=None, context_line=False):
if enchant_import_error is not None:
raise RuntimeError(
'Cannot instantiate SpellingChecker '
'without PyEnchant installed',
) from enchant_import_error
if filters is None:
filters = []
self.dictionary = enchant.DictWithPWL(lang, word_list_filename)
Expand Down
13 changes: 5 additions & 8 deletions sphinxcontrib/spelling/directive.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
from docutils.parsers import rst
from sphinx.util import logging

from . import filters

logger = logging.getLogger(__name__)


Expand All @@ -31,9 +29,9 @@ class SpellingDirective(rst.Directive):
def run(self):
env = self.state.document.settings.env

# Initialize the per-document filters
if not hasattr(env, 'spelling_document_filters'):
env.spelling_document_filters = collections.defaultdict(list)
# Initialize the per-document good words list
if not hasattr(env, 'spelling_document_words'):
env.spelling_document_words = collections.defaultdict(list)

good_words = []
for entry in self.content:
Expand All @@ -45,7 +43,6 @@ def run(self):
'Extending local dictionary for %s with %s' % (
env.docname, str(good_words))
)
env.spelling_document_filters[env.docname].append(
filters.IgnoreWordsFilterFactory(good_words)
)
env.spelling_document_words[env.docname].extend(good_words)

return []
Loading

0 comments on commit a98b54b

Please sign in to comment.