├── .git_archival.txt
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── check.yml
│ ├── history-update.yaml
│ ├── integration.yaml
│ ├── python-publish.yaml
│ └── test.yml
├── .gitignore
├── .mergify.yml
├── .readthedocs.yml
├── .testr.conf
├── .virtualenvwrapper
├── postactivate
└── predeactivate
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── docs
├── Makefile
├── requirements.txt
└── source
│ ├── conf.py
│ ├── customize.rst
│ ├── developers.rst
│ ├── history.rst
│ ├── index.rst
│ ├── install.rst
│ ├── run.rst
│ └── spelling_wordlist.txt
├── integration_tests
└── build_django.py
├── pyproject.toml
├── requirements-dev.txt
├── show-changelog.sh
├── sphinxcontrib
└── spelling
│ ├── __init__.py
│ ├── asset.py
│ ├── builder.py
│ ├── checker.py
│ ├── directive.py
│ ├── domain.py
│ ├── filters.py
│ └── role.py
├── tests
├── helpers.py
├── test_builder.py
├── test_checker.py
├── test_filter.py
└── test_wordlist.txt
└── tools
└── history-update.sh
/.git_archival.txt:
--------------------------------------------------------------------------------
1 | # https://github.com/pypa/setuptools_scm
2 | node: 322ad6e91130791785e82daaa0138dda9d7d47db
3 | node-date: 2025-05-25T11:53:34-04:00
4 | describe-name: 8.0.1-9-g322ad6e9
5 | ref-names: HEAD -> main
6 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | .git_archival.txt export-subst
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Via https://github.com/nedbat/scriv/blob/main/.github/dependabot.yml
2 | #
3 | # From:
4 | # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/keeping-your-actions-up-to-date-with-dependabot
5 | # Set update schedule for GitHub Actions
6 |
7 | version: 2
8 | updates:
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | # Check for updates to GitHub Actions once a week
13 | interval: "weekly"
14 |
--------------------------------------------------------------------------------
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | name: Check
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | if: ${{ !startsWith(github.ref, 'refs/tags') }}
11 |
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | hatch-environment:
16 | - docs:build
17 | - docs:check
18 | - test:lint
19 | - test:pkglint
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 | with:
24 | fetch-depth: 0
25 |
26 | - name: Set up Python
27 | uses: actions/setup-python@v5
28 | with:
29 | python-version: '3.x'
30 |
31 | - name: Install dependencies
32 | run: python -m pip install hatch
33 |
34 | - name: Run
35 | run: hatch run ${{ matrix.hatch-environment }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/history-update.yaml:
--------------------------------------------------------------------------------
1 | name: History Update
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | history-update:
9 | runs-on: ubuntu-latest
10 | if: ${{ !startsWith(github.ref, 'refs/tags') }}
11 |
12 | strategy:
13 | fail-fast: false
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | # Get all of the git history. https://github.com/actions/checkout
18 | with:
19 | fetch-depth: 0
20 |
21 | - name: Check for history.rst update
22 | if: startsWith(github.ref, 'refs/tags') != true
23 | run: ./tools/history-update.sh
24 |
--------------------------------------------------------------------------------
/.github/workflows/integration.yaml:
--------------------------------------------------------------------------------
1 | name: Integration Tests
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | django:
9 | runs-on: ubuntu-latest
10 | if: ${{ !startsWith(github.ref, 'refs/tags') }}
11 |
12 | strategy:
13 | fail-fast: false
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v5
22 | with:
23 | python-version: '3.x'
24 |
25 | - name: Install dependencies
26 | run: python3 -m pip install hatch
27 |
28 | - name: Test
29 | run: hatch run integration:django
30 |
--------------------------------------------------------------------------------
/.github/workflows/python-publish.yaml:
--------------------------------------------------------------------------------
1 | # This workflows will upload a Python Package using Twine when a release is created
2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | name: Upload Python Package
5 |
6 | on:
7 | - push
8 |
9 | jobs:
10 | build-n-publish:
11 | name: Build and publish Python distributions to PyPI
12 | if: ${{ github.repository_owner == 'sphinx-contrib' }}
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | with:
19 | fetch-depth: 0
20 | - name: Set up Python
21 | uses: actions/setup-python@v5
22 | with:
23 | python-version: '3.x'
24 | - name: Install dependencies
25 | run: |
26 | python -m pip install --upgrade pip
27 | pip install setuptools wheel twine build setuptools_scm
28 | - name: Build sdist and wheel
29 | run: |
30 | python -m build
31 | # - name: Publish distribution to Test PyPI
32 | # uses: pypa/gh-action-pypi-publish@master
33 | # with:
34 | # password: ${{ secrets.test_pypi_password }}
35 | # repository_url: https://test.pypi.org/legacy/
36 | - name: Publish distribution to PyPI
37 | if: startsWith(github.ref, 'refs/tags')
38 | uses: pypa/gh-action-pypi-publish@master
39 | with:
40 | password: ${{ secrets.pypi_password }}
41 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | if: ${{ !startsWith(github.ref, 'refs/tags') }}
11 |
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | python-version:
16 | - '3.10'
17 | - '3.11'
18 | - '3.12'
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 | with:
23 | fetch-depth: 0
24 |
25 | - name: Set up Python ${{ matrix.python-version }}
26 | uses: actions/setup-python@v5
27 | with:
28 | python-version: ${{ matrix.python-version }}
29 |
30 | - name: Install dependencies
31 | run: python -m pip install hatch
32 |
33 | - name: Run tests
34 | run: hatch run test:test
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.egg
3 | *.so
4 | build/
5 | dist/
6 | env/
7 | *.egg-info/
8 | TAGS
9 | *~
10 | .DS_Store
11 | .idea
12 | .vscode
13 | spelling/spelling_wordlist.txt
14 | .tox/
15 | .coverage/
16 | *.swp
17 |
18 | # Created by pbr
19 | AUTHORS
20 | ChangeLog
21 | /.coverage
22 | /.testrepository/
23 | /cover/
24 | .eggs/
25 | # Generated by setuptools_scm
26 | sphinxcontrib/spelling/version.py
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | pull_request_rules:
2 | - name: Add CI label
3 | conditions:
4 | - or:
5 | - "title~=^tox:"
6 | - "title~=^ci:"
7 | - "files~=tox.ini"
8 | - "files~=.github/"
9 | actions:
10 | label:
11 | add:
12 | - ci
13 |
14 | - name: Add Mergify label
15 | conditions:
16 | - or:
17 | - "title~=^mergify:"
18 | - "files~=.mergify.yml$"
19 | actions:
20 | label:
21 | add:
22 | - mergify
23 |
24 | - name: Automatic merge on approval
25 | conditions:
26 | - and:
27 | - "check-success=build (docs:build)"
28 | - "check-success=build (test:linter)"
29 | - "check-success=build (docs:spelling)"
30 | - "check-success=django"
31 | - "check-success=build (3.10)"
32 | - "check-success=build (3.11)"
33 | - "check-success=build (3.12)"
34 | - "-draft"
35 | - or:
36 | - "check-success=history-update"
37 | - "label=ci"
38 | - or:
39 | - "approved-reviews-by=dhellmann"
40 | - "author=dhellmann"
41 | actions:
42 | merge:
43 | method: merge
44 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | # https://docs.readthedocs.io/en/stable/config-file/v2.html
2 |
3 | # Required
4 | version: 2
5 |
6 | sphinx:
7 | configuration: docs/source/conf.py
8 |
9 | python:
10 | install:
11 | - requirements: docs/requirements.txt
12 |
--------------------------------------------------------------------------------
/.testr.conf:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | test_command=${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
3 | test_id_option=--load-list $IDFILE
4 | test_list_option=--list
5 |
--------------------------------------------------------------------------------
/.virtualenvwrapper/postactivate:
--------------------------------------------------------------------------------
1 | # -*- shell-script -*-
2 |
3 | #export PYENCHANT_LIBRARY_PATH=/opt/homebrew/Cellar/enchant/2.6.4/lib/libenchant-2.2.dylib
4 | if [ $(uname) = Darwin ]; then
5 | export PYENCHANT_LIBRARY_PATH=$(brew list enchant | grep 'libenchant-.*\.dylib' | head -n 1)
6 | fi
7 |
--------------------------------------------------------------------------------
/.virtualenvwrapper/predeactivate:
--------------------------------------------------------------------------------
1 | # -*- shell-script -*-
2 |
3 | unset PYENCHANT_LIBRARY_PATH
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 by Doug Hellmann.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README
2 | include LICENSE
3 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Default target is to show help
2 | help:
3 | @echo "sdist - Source distribution"
4 | @echo "html - HTML documentation"
5 | @echo "spelling - Check spelling of documentation"
6 | @echo "upload - upload a new release to PyPI"
7 | @echo "installwebsite - deploy web version of docs"
8 | @echo "develop - install development version"
9 | @echo "test - run the test suite"
10 |
11 |
12 | .PHONY: sdist
13 | sdist: html
14 | python setup.py sdist
15 |
16 | .PHONY: upload
17 | upload: html
18 | python setup.py sdist upload
19 |
20 | # Documentation
21 | .PHONY: html
22 | html:
23 | (cd docs && $(MAKE) html)
24 |
25 | .PHONY: spelling
26 | spelling:
27 | (cd docs && $(MAKE) spelling)
28 |
29 | installwebsite: html
30 | (cd docs/build/html && rsync --rsh=ssh --archive --delete --verbose . www.doughellmann.com:/var/www/doughellmann/DocumentRoot/docs/sphinxcontrib.spelling/)
31 |
32 | # Register the new version on pypi
33 | .PHONY: register
34 | register:
35 | python setup.py register
36 |
37 | # Testing
38 | .PHONY: test
39 | test:
40 | tox
41 |
42 | develop:
43 | python setup.py develop
44 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | .. -*- mode: rst -*-
2 |
3 | =========================
4 | sphinxcontrib-spelling
5 | =========================
6 |
7 | This package contains sphinxcontrib.spelling, a spelling checker for
8 | Sphinx-based documentation. It uses PyEnchant_ to produce a report
9 | showing misspelled words.
10 |
11 | Refer to the `main documentation page
12 | `__ for
13 | installation and setup details.
14 |
15 | License
16 | =======
17 |
18 | Copyright Doug Hellmann, All Rights Reserved
19 |
20 | Permission to use, copy, modify, and distribute this software and its
21 | documentation for any purpose and without fee is hereby granted,
22 | provided that the above copyright notice appear in all copies and that
23 | both that copyright notice and this permission notice appear in
24 | supporting documentation, and that the name of Doug Hellmann not be used
25 | in advertising or publicity pertaining to distribution of the software
26 | without specific, written prior permission.
27 |
28 | DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
29 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
30 | EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
31 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
32 | USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
33 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
34 | PERFORMANCE OF THIS SOFTWARE.
35 |
36 | .. _PyEnchant: https://github.com/pyenchant/pyenchant
37 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
14 |
15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
16 |
17 | help:
18 | @echo "Please use \`make ' where is one of"
19 | @echo " html to make standalone HTML files"
20 | @echo " dirhtml to make HTML files named index.html in directories"
21 | @echo " singlehtml to make a single large HTML file"
22 | @echo " pickle to make pickle files"
23 | @echo " json to make JSON files"
24 | @echo " htmlhelp to make HTML files and a HTML help project"
25 | @echo " qthelp to make HTML files and a qthelp project"
26 | @echo " devhelp to make HTML files and a Devhelp project"
27 | @echo " epub to make an epub"
28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
29 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
30 | @echo " text to make text files"
31 | @echo " man to make manual pages"
32 | @echo " changes to make an overview of all changed/added/deprecated items"
33 | @echo " linkcheck to check all external links for integrity"
34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
35 |
36 | clean:
37 | -rm -rf $(BUILDDIR)/*
38 |
39 | html:
40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
41 | @echo
42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
43 |
44 | dirhtml:
45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
48 |
49 | singlehtml:
50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
51 | @echo
52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
53 |
54 | pickle:
55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
56 | @echo
57 | @echo "Build finished; now you can process the pickle files."
58 |
59 | json:
60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
61 | @echo
62 | @echo "Build finished; now you can process the JSON files."
63 |
64 | htmlhelp:
65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
66 | @echo
67 | @echo "Build finished; now you can run HTML Help Workshop with the" \
68 | ".hhp project file in $(BUILDDIR)/htmlhelp."
69 |
70 | qthelp:
71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
72 | @echo
73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/sphinxcontribspelling.qhcp"
76 | @echo "To view the help file:"
77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sphinxcontribspelling.qhc"
78 |
79 | devhelp:
80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
81 | @echo
82 | @echo "Build finished."
83 | @echo "To view the help file:"
84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/sphinxcontribspelling"
85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sphinxcontribspelling"
86 | @echo "# devhelp"
87 |
88 | epub:
89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
90 | @echo
91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
92 |
93 | latex:
94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
95 | @echo
96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
98 | "(use \`make latexpdf' here to do that automatically)."
99 |
100 | latexpdf:
101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
102 | @echo "Running LaTeX files through pdflatex..."
103 | make -C $(BUILDDIR)/latex all-pdf
104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
105 |
106 | text:
107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
108 | @echo
109 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
110 |
111 | man:
112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
113 | @echo
114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
115 |
116 | changes:
117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
118 | @echo
119 | @echo "The overview file is in $(BUILDDIR)/changes."
120 |
121 | linkcheck:
122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
123 | @echo
124 | @echo "Link check complete; look for any errors in the above output " \
125 | "or in $(BUILDDIR)/linkcheck/output.txt."
126 |
127 | spelling:
128 | $(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) $(BUILDDIR)/spelling
129 |
130 | doctest:
131 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
132 | @echo "Testing of doctests in the sources finished, look at the " \
133 | "results in $(BUILDDIR)/doctest/output.txt."
134 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file is only needed for readthedocs.org
2 | .[docs]
3 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # sphinxcontrib.spelling documentation build configuration file, created by
2 | # sphinx-quickstart on Sun Apr 17 15:33:23 2011.
3 | #
4 | # This file is execfile()d with the current directory set to its containing dir.
5 | #
6 | # Note that not all possible configuration values are present in this
7 | # autogenerated file.
8 | #
9 | # All configuration values have a default; values that are commented out
10 | # serve to show the default.
11 | import os
12 | import sys
13 |
14 | # If extensions (or modules to document with autodoc) are in another directory,
15 | # add these directories to sys.path here. If the directory is relative to the
16 | # documentation root, use os.path.abspath to make it absolute, like shown here.
17 | # sys.path.insert(0, os.path.abspath('.'))
18 |
19 | # -- General configuration -----------------------------------------------------
20 |
21 | # If your documentation needs a minimal Sphinx version, state it here.
22 | # needs_sphinx = '1.0'
23 |
24 | # Add any Sphinx extension module names here, as strings. They can be extensions
25 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
26 | extensions = [
27 | "sphinxcontrib.spelling",
28 | ]
29 |
30 | spelling_word_list_filename = [
31 | "spelling_wordlist.txt",
32 | ]
33 |
34 | spelling_show_suggestions = True
35 | spelling_ignore_pypi_package_names = True
36 | spelling_ignore_contributor_names = True
37 |
38 | # Add any paths that contain templates here, relative to this directory.
39 | templates_path = ["_templates"]
40 |
41 | # The suffix of source filenames.
42 | source_suffix = ".rst"
43 |
44 | # The encoding of source files.
45 | # source_encoding = 'utf-8-sig'
46 |
47 | # The master toctree document.
48 | master_doc = "index"
49 |
50 | # General information about the project.
51 | project = "sphinxcontrib.spelling"
52 | copyright = "2011, Doug Hellmann"
53 |
54 | # The version info for the project you're documenting, acts as replacement for
55 | # |version| and |release|, also used in various other places throughout the
56 | # built documents.
57 | #
58 | # The short X.Y version.
59 | version = "1.4"
60 | # The full version, including alpha/beta/rc tags.
61 | release = version
62 |
63 | # The language for content autogenerated by Sphinx. Refer to documentation
64 | # for a list of supported languages.
65 | language = "en"
66 |
67 | # There are two options for replacing |today|: either, you set today to some
68 | # non-false value, then it is used:
69 | # today = ''
70 | # Else, today_fmt is used as the format for a strftime call.
71 | # today_fmt = '%B %d, %Y'
72 |
73 | # List of patterns, relative to source directory, that match files and
74 | # directories to ignore when looking for source files.
75 | exclude_patterns = []
76 |
77 | # The reST default role (used for this markup: `text`) to use for all documents.
78 | # default_role = None
79 |
80 | # If true, '()' will be appended to :func: etc. cross-reference text.
81 | # add_function_parentheses = True
82 |
83 | # If true, the current module name will be prepended to all description
84 | # unit titles (such as .. function::).
85 | # add_module_names = True
86 |
87 | # If true, sectionauthor and moduleauthor directives will be shown in the
88 | # output. They are ignored by default.
89 | # show_authors = False
90 |
91 | # The name of the Pygments (syntax highlighting) style to use.
92 | pygments_style = "sphinx"
93 |
94 | # A list of ignored prefixes for module index sorting.
95 | # modindex_common_prefix = []
96 |
97 |
98 | # -- Options for HTML output ---------------------------------------------------
99 |
100 | # The theme to use for HTML and HTML Help pages. See the documentation for
101 | # a list of builtin themes.
102 | html_theme = "default"
103 |
104 | # Theme options are theme-specific and customize the look and feel of a theme
105 | # further. For a list of options available for each theme, see the
106 | # documentation.
107 | # html_theme_options = {}
108 |
109 | # Add any paths that contain custom themes here, relative to this directory.
110 | # html_theme_path = []
111 |
112 | # The name for this set of Sphinx documents. If None, it defaults to
113 | # " v documentation".
114 | # html_title = None
115 |
116 | # A shorter title for the navigation bar. Default is the same as html_title.
117 | # html_short_title = None
118 |
119 | # The name of an image file (relative to this directory) to place at the top
120 | # of the sidebar.
121 | # html_logo = None
122 |
123 | # The name of an image file (within the static path) to use as favicon of the
124 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
125 | # pixels large.
126 | # html_favicon = None
127 |
128 | # Add any paths that contain custom static files (such as style sheets) here,
129 | # relative to this directory. They are copied after the builtin static files,
130 | # so a file named "default.css" will overwrite the builtin "default.css".
131 | # html_static_path = ['_static']
132 |
133 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
134 | # using the given strftime format.
135 | # html_last_updated_fmt = '%b %d, %Y'
136 |
137 | # If true, SmartyPants will be used to convert quotes and dashes to
138 | # typographically correct entities.
139 | # html_use_smartypants = True
140 |
141 | # Custom sidebar templates, maps document names to template names.
142 | # html_sidebars = {}
143 |
144 | # Additional templates that should be rendered to pages, maps page names to
145 | # template names.
146 | # html_additional_pages = {}
147 |
148 | # If false, no module index is generated.
149 | # html_domain_indices = True
150 |
151 | # If false, no index is generated.
152 | # html_use_index = True
153 |
154 | # If true, the index is split into individual pages for each letter.
155 | # html_split_index = False
156 |
157 | # If true, links to the reST sources are added to the pages.
158 | # html_show_sourcelink = True
159 |
160 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
161 | # html_show_sphinx = True
162 |
163 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
164 | # html_show_copyright = True
165 |
166 | # If true, an OpenSearch description file will be output, and all pages will
167 | # contain a tag referring to it. The value of this option must be the
168 | # base URL from which the finished HTML is served.
169 | # html_use_opensearch = ''
170 |
171 | # This is the file name suffix for HTML files (e.g. ".xhtml").
172 | # html_file_suffix = None
173 |
174 | # Output file base name for HTML help builder.
175 | htmlhelp_basename = "sphinxcontribspellingdoc"
176 |
177 |
178 | # -- Options for LaTeX output --------------------------------------------------
179 |
180 | # The paper size ('letter' or 'a4').
181 | # latex_paper_size = 'letter'
182 |
183 | # The font size ('10pt', '11pt' or '12pt').
184 | # latex_font_size = '10pt'
185 |
186 | # Grouping the document tree into LaTeX files. List of tuples
187 | # (source start file, target name, title, author, documentclass [howto/manual]).
188 | latex_documents = [
189 | (
190 | "index",
191 | "sphinxcontribspelling.tex",
192 | "sphinxcontrib.spelling Documentation",
193 | "Doug Hellmann",
194 | "manual",
195 | ),
196 | ]
197 |
198 | # The name of an image file (relative to this directory) to place at the top of
199 | # the title page.
200 | # latex_logo = None
201 |
202 | # For "manual" documents, if this is true, then toplevel headings are parts,
203 | # not chapters.
204 | # latex_use_parts = False
205 |
206 | # If true, show page references after internal links.
207 | # latex_show_pagerefs = False
208 |
209 | # If true, show URL addresses after external links.
210 | # latex_show_urls = False
211 |
212 | # Additional stuff for the LaTeX preamble.
213 | # latex_preamble = ''
214 |
215 | # Documents to append as an appendix to all manuals.
216 | # latex_appendices = []
217 |
218 | # If false, no module index is generated.
219 | # latex_domain_indices = True
220 |
221 |
222 | # -- Options for manual page output --------------------------------------------
223 |
224 | # One entry per manual page. List of tuples
225 | # (source start file, name, description, authors, manual section).
226 | man_pages = [
227 | (
228 | "index",
229 | "sphinxcontribspelling",
230 | "sphinxcontrib.spelling Documentation",
231 | ["Doug Hellmann"],
232 | 1,
233 | )
234 | ]
235 |
--------------------------------------------------------------------------------
/docs/source/customize.rst:
--------------------------------------------------------------------------------
1 | =======================
2 | Configuration Options
3 | =======================
4 |
5 | These options can be set in ``conf.py`` along with the other Sphinx
6 | configuration settings.
7 |
8 | Input Options
9 | =============
10 |
11 | ``spelling_lang='en_US'``
12 |
13 | String specifying the language, as understood by PyEnchant and
14 | enchant. Defaults to ``en_US`` for US English.
15 |
16 | ``tokenizer_lang='en_US'``
17 |
18 | String specifying the tokenizer language as understood by PyEnchant
19 | and enchant. Defaults to ``en_US`` for US English.
20 |
21 | ``spelling_word_list_filename='spelling_wordlist.txt'``
22 |
23 | String specifying a file containing a list of words known to be
24 | spelled correctly but that do not appear in the language dictionary
25 | selected by ``spelling_lang``. The file should contain one word per
26 | line. Refer to the `PyEnchant tutorial`_ for details.
27 |
28 | To add multiple files use a list, or a comma separated string. This
29 | is useful when calling sphinx with ``-D
30 | spelling_word_list_filename=...`` which will not accept a list and
31 | will only accept a string parameter.
32 |
33 | ``spelling_word_list_filename=['spelling_wordlist.txt','another_list.txt']``
34 |
35 | Same as above, but with several files of correctly spelled words.
36 |
37 | ``spelling_word_list_filename='spelling_wordlist.txt,another_list.txt'``
38 |
39 | Same as above, but with several files of correctly spelled words, and
40 | passing the setting as a single string.
41 |
42 | ``spelling_exclude_patterns=['ignored_*']``
43 |
44 | A list of glob-style patterns that should be ignored when checking spelling.
45 | They are matched against the source file names relative to the source
46 | directory, using slashes as directory separators on all platforms. See Sphinx's
47 | `exclude_patterns`_ option for more details on glob-style patterns.
48 |
49 | .. _PyEnchant tutorial: https://github.com/rfk/pyenchant/blob/master/website/content/tutorial.rst
50 | .. _exclude_patterns : https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-exclude_patterns
51 |
52 | .. _output-options:
53 |
54 | Output Options
55 | ==============
56 |
57 | ``spelling_show_suggestions=False``
58 |
59 | Boolean controlling whether suggestions for misspelled words are
60 | printed. Defaults to ``False``.
61 |
62 | ``spelling_suggestion_limit=0``
63 |
64 | Integer number of suggestions to emit when
65 | ``spelling_show_suggestions`` is ``True``. Defaults to ``0``,
66 | meaning no limit. Any positive value truncates the suggestion limit.
67 |
68 | ``spelling_show_whole_line=True``
69 |
70 | Boolean controlling whether the contents of the line containing each
71 | misspelled word is printed, for more context about the location of each
72 | word. Defaults to True.
73 |
74 | ``spelling_warning=False``
75 |
76 | Boolean controlling whether a misspelling is emitted as a sphinx
77 | warning or as an info message. Defaults to False.
78 |
79 | ``spelling_verbose=True``
80 |
81 | Choose whether or not the misspelled words should be displayed in
82 | the terminal. Defaults to True.
83 |
84 | Word Filters
85 | ============
86 |
87 | Enable or disable the built-in filters to control which words are
88 | returned by the tokenizer to be checked.
89 |
90 | ``spelling_ignore_pypi_package_names=False``
91 |
92 | Boolean controlling whether words that look like package names from
93 | PyPI are treated as spelled properly. When ``True``, the current
94 | list of package names is downloaded at the start of the build and
95 | used to extend the list of known words in the dictionary. Defaults
96 | to ``False``.
97 |
98 | ``spelling_ignore_wiki_words=True``
99 |
100 | Boolean controlling whether words that follow the CamelCase
101 | conventions used for page names in :spelling:word:`wikis` should be treated as
102 | spelled properly. Defaults to ``True``.
103 |
104 | ``spelling_ignore_acronyms=True``
105 |
106 | Boolean controlling treatment of words that appear in all capital
107 | letters, or all capital letters followed by a lower case ``s``. When
108 | ``True``, acronyms are assumed to be spelled properly. Defaults to
109 | ``True``.
110 |
111 | ``spelling_ignore_python_builtins=True``
112 |
113 | Boolean controlling whether names built in to Python should be
114 | treated as spelled properly. Defaults to ``True``.
115 |
116 | ``spelling_ignore_importable_modules=True``
117 |
118 | Boolean controlling whether words that are names of modules found on
119 | ``sys.path`` are treated as spelled properly. Defaults to ``True``.
120 |
121 | ``spelling_ignore_contributor_names=True``
122 |
123 | Boolean controlling whether contributor names taken from the git
124 | history for the repository are considered as spelled correctly.
125 |
126 | ``spelling_filters=[]``
127 |
128 | List of importable filter classes to be added to the tokenizer that
129 | produces words to be checked. For example,
130 | ``["enchant.tokenize.MentionFilter"]``. The classes should be
131 | derived from ``enchant.tokenize.Filter``. Refer to the `PyEnchant
132 | tutorial`_ for examples.
133 |
134 | Managing Lists of Correctly Spelled Words and Ignoring Words
135 | ============================================================
136 |
137 | There are three ways to provide a list of known good words. The
138 | ``spelling_word_list_filename`` option (described above) specifies the
139 | name of a plain text file containing one word per line. All of the
140 | words in the file are assumed to be spelled correctly and may appear
141 | in any part of the document being processed.
142 |
143 | You can use multiple text files with words to be added to the dictionary,
144 | to do this all you need to do is use a list and include the name of your
145 | text files.
146 |
147 | For example::
148 |
149 | spelling_word_list_filename = ['spelling_wordlist.txt', 'my_wordlist.txt']
150 |
151 | The ``spelling:word-list`` directive can be used to create a list of
152 | words known to be spelled correctly within a single file. For
153 | example, if a document refers to a person or project by name, the name
154 | can be added to the list of known words for just that document.
155 |
156 | ::
157 |
158 | .. spelling:word-list::
159 |
160 | Docutils
161 | Goodger
162 |
163 | The ``spelling:word`` role can be used to annotate individual words as
164 | being spelled correctly throughout a single document.
165 |
166 |
167 | ::
168 |
169 | This text refers to :spelling:word:`Goodger`.
170 |
171 | The ``spelling:ignore`` role can be used to ignore a single instance
172 | of a word.
173 |
174 | ::
175 |
176 | This text refers to :spelling:ignore:`docutils`.
177 |
178 | .. _PyEnchant: https://github.com/rfk/pyenchant
179 |
180 | Custom Word Filters
181 | ===================
182 |
183 | The PyEnchant tokenizer supports a "filtering" API for processing
184 | words from the input. Filters can alter the stream of words by adding,
185 | replacing, or dropping values.
186 |
187 | New filters should be derived from ``enchant.tokenize.Filter`` and
188 | implement either the ``_split()`` method (to add or replace words) or
189 | ``_skip()`` (to treat words as being spelled correctly). For example,
190 | this :class:`AcronymFilter` skips words that are all uppercase letters
191 | or all uppercase with a trailing lowercase "s".
192 |
193 | ::
194 |
195 | class AcronymFilter(Filter):
196 | """If a word looks like an acronym (all upper case letters),
197 | ignore it.
198 | """
199 |
200 | def _skip(self, word):
201 | return (word.isupper() # all caps
202 | or
203 | # pluralized acronym ("URLs")
204 | (word[-1].lower() == 's'
205 | and
206 | word[:-1].isupper()
207 | )
208 | )
209 |
210 | To be used in a document, the custom filter needs to be installed
211 | somewhere that Sphinx can import it while processing the input
212 | files. The Sphinx project's ``conf.py`` then needs two changes.
213 |
214 | 1. Import the filter class.
215 | 2. Add the import string for the filter class to the
216 | ``spelling_filters`` configuration variable.
217 |
218 | ::
219 |
220 | spelling_filters = ['mymodule.MyFilter']
221 |
222 | .. seealso::
223 |
224 | * `Creating a Spelling Checker for reStructuredText Documents
225 | `_
226 | * `PyEnchant tutorial`_
227 |
--------------------------------------------------------------------------------
/docs/source/developers.rst:
--------------------------------------------------------------------------------
1 | .. spelling:word-list::
2 |
3 | sphinxcontrib
4 | reStructuredText
5 |
6 | ============
7 | Developers
8 | ============
9 |
10 | If you would like to contribute to sphinxcontrib.spelling directly,
11 | these instructions should help you get started. Patches, bug reports,
12 | and feature requests are all welcome through the `GitHub site
13 | `__.
14 | Contributions in the form of patches or pull requests are easier to
15 | integrate and will receive priority attention.
16 |
17 | Running tests
18 | =============
19 |
20 | To run the tests, you need ``tox`` installed, then just run
21 | ``tox``. This should run the unit tests, the source code linter, and
22 | try to build the current documentation.
23 |
24 | Enchant C Library
25 | -----------------
26 |
27 | You also need the C library from Enchant installed. On macOS, use
28 | `brew` to install the `enchant` package, then set
29 | `PYENCHANT_LIBRARY_PATH` to point to the `dylib` file included in the
30 | output of `brew list enchant`.
31 |
32 | .. code-block:: console
33 |
34 | $ brew list enchant | grep dylib
35 | /opt/homebrew/Cellar/enchant/2.6.4/lib/libenchant-2.dylib
36 | /opt/homebrew/Cellar/enchant/2.6.4/lib/libenchant-2.2.dylib
37 |
38 | $ export PYENCHANT_LIBRARY_PATH=/opt/homebrew/Cellar/enchant/2.6.4/lib/libenchant-2.2.dylib
39 |
40 | Coding style
41 | ============
42 |
43 | Python imports are formatted and sorted using `isort
44 | `__. To format all files, run:
45 |
46 | .. code-block:: console
47 |
48 | $ tox -e style
49 |
50 | Building Documentation
51 | ======================
52 |
53 | The documentation for sphinxcontrib.spelling is written in
54 | reStructuredText and converted to HTML using Sphinx. The build is
55 | driven by ``tox``. To build only the documentation, run ``tox -e
56 | docs``.
57 |
58 | Contributing
59 | ============
60 |
61 | Please submit changes as pull requests using the `GitHub repository
62 | `__.
63 |
64 | In the pull request description, link to any issues closed by the
65 | changes using ``Fixes #NUM``, replacing ``NUM`` with the issue number.
66 |
67 | Release Notes
68 | =============
69 |
70 | Please add a release note for each pull request to ``docs/history.rst``.
71 |
--------------------------------------------------------------------------------
/docs/source/history.rst:
--------------------------------------------------------------------------------
1 | =================
2 | Release History
3 | =================
4 |
5 | .. spelling:word-list::
6 |
7 | arg
8 | commandline
9 | config
10 | docstring
11 | emacs
12 | env
13 | Homebrew
14 | libenchant
15 | macOS
16 | namespace
17 | repo
18 | scm
19 | setuptools
20 | testrepository
21 | unicode
22 | unmaintained
23 |
24 | Next
25 | ====
26 |
27 | - Modernize packaging using hatch and hatchling.
28 |
29 | Bug Fixes
30 | ---------
31 |
32 | - `#229 `__ Gracefully
33 | handle if git is not installed
34 | - `#227 `__ Use pypi.org's
35 | JSON API instead of XML-RPC.
36 |
37 | 7.7.0
38 | =====
39 |
40 | New Features
41 | ------------
42 |
43 | - `#199 `__ Add
44 | ``spelling:ignore`` role for marking inline text to not be
45 | checked. See :doc:`/customize` for more details.
46 |
47 | 7.6.2
48 | =====
49 |
50 | Bug Fixes
51 | ---------
52 |
53 | - `#193 `__
54 | Remove excessive debug printing in `:spelling:word:` and `spelling` builder
55 | implementations.
56 |
57 | 7.6.1
58 | =====
59 |
60 | Bug Fixes
61 | ---------
62 |
63 | - `#188 `__
64 | Fix `:spelling:word:` directives from being printed verbatim in
65 | output files.
66 |
67 | 7.6.0
68 | =====
69 |
70 | Features
71 | --------
72 |
73 | - Convert to use Sphinx domains. Add ``spelling:word-list``
74 | directive. Have ``spelling`` directive report that it is deprecated.
75 | - Add ``spelling:word`` role for marking inline text as spelled
76 | correctly.
77 |
78 | 7.5.1
79 | =====
80 |
81 | Bug Fixes
82 | ---------
83 |
84 | - `#180 `__
85 | Suppress `SystemExit` errors in `ImportableModuleFilter` caused by
86 | importing modules that run code on import and exit when that code
87 | sees an error. Bug report and reproducer provided by Trevor Gross.
88 |
89 | 7.5.0
90 | =====
91 |
92 | Features
93 | --------
94 |
95 | - `#151 `__
96 | Added configuration option to limit the number of suggestions
97 | output. See :doc:`/customize` for more details. Idea contributed by
98 | Trevor Gross.
99 | - `#169 `__
100 | Adds the ability to pass in multiple wordlists via the sphinx
101 | command line as ``-D spelling_word_list_filename=file1,file2``.
102 |
103 | Bug Fixes
104 | ---------
105 |
106 | - `#36 `__
107 | Include captions of figures in the set of nodes for which the text
108 | is checked.
109 |
110 | 7.4.1
111 | =====
112 |
113 | - `#160 `__
114 | Fixed issue with the builder crashing when reporting a misspelled word
115 | in a python docstring.
116 |
117 | 7.4.0
118 | =====
119 |
120 | - Fix a problem that occurred when the extra word list is empty and an
121 | IndexError is thrown. Prevent the error by checking the contents of
122 | the file before using the list.
123 | - `#153 `__
124 | Ensure the correct relative filename is reported as the location of
125 | a misspelled word when the word is in an included file. Log the
126 | location ourselves instead of letting the logging system compute it
127 | for consistency until `the fix
128 | `__ is merged into
129 | Sphinx.
130 | - Change default env list for local tox runs to only include the
131 | current python version, as defined by the installation of tox.
132 | - Tell tox to pass `PYENCHANT_LIBRARY_PATH` through to commands. On
133 | macOS it can be a little tricky to set up libenchant if your default
134 | python does not match the one used by Homebrew for the
135 | library. Setting the variable to point to the library fixes that,
136 | but we don't want to set it in this file for everyone so use
137 | `passenv` to tell tox to pass the setting through when running the
138 | commands for each env.
139 | - `#159 `__
140 | Report using the line number of the misspelled word instead of using
141 | the first line of the node, in both the log and `.spelling` output
142 | file.
143 |
144 | 7.3.3
145 | =====
146 |
147 | Bug Fixes
148 | ---------
149 |
150 | - `#149 `__ Fixes
151 | to support testing when building RPMs. Switch to PEP 420 native
152 | namespace and skip contributors test when not in a git repo.
153 | - `#150 `__ Minor
154 | code cleanup primarily around string interpolation.
155 |
156 | 7.3.2
157 | =====
158 |
159 | Bug Fixes
160 | ---------
161 |
162 | - `#143 `__ Treat
163 | ``__main__`` as a special module name that cannot be imported. If
164 | the test suite is invoked by running ``python -m pytest`` instead of
165 | ``pytest`` then there will be no ``__main__`` and find_spec() will
166 | fail, so this change makes the tests work in both modes.
167 | - `#144 `__ Fix
168 | python filename handling in ``ImportableModuleFilter``. If the word
169 | looks like a python module filename, strip the extension to avoid
170 | the side-effect of actually importing the module. This prevents, for
171 | example, ``'setup.py'`` triggering an import of the ``setup`` module
172 | during a doc build, which makes it look like Sphinx is complaining
173 | about a commandline argument.
174 |
175 | 7.3.1
176 | =====
177 |
178 | Bug Fixes
179 | ---------
180 |
181 | - `#137 `__
182 | replace the use of deprecated ``imp`` in ``ImportableModuleFilter``
183 | with ``importlib``
184 |
185 | 7.3.0
186 | =====
187 |
188 | New Features
189 | ------------
190 |
191 | - `#131 `__
192 | included a documentation update to fix a broken link.
193 |
194 | - `#130 `__ tested support
195 | for Python 3.10, and added the trove classifier.
196 |
197 | - `#129 `__ improved the
198 | speed of the ``ImportableModuleFilter``.
199 |
200 | - `#128 `__ fixed
201 | some issues with the packaging configuration.
202 |
203 | 7.2.0
204 | =====
205 |
206 | New Features
207 | ------------
208 |
209 | - `#123 `__ adds
210 | the ``spelling_verbose`` configuration option for controlling
211 | whether misspelled words are printed to the console as well as the
212 | output log files. See :ref:`output-options` for details.
213 |
214 | 7.1.0
215 | =====
216 |
217 | New Features
218 | ------------
219 |
220 | - `#116 `__ adds
221 | a config option `spelling_warning` that makes individual messages
222 | about misspellings warnings. The same change also updates the
223 | formatting of the message to make it easier for IDEs to parse,
224 | allowing the editor to navigate to the location of the misspelled
225 | word. See :ref:`output-options` for details. Contributed by Robert
226 | Cohn.
227 |
228 | 7.0.1
229 | =====
230 |
231 | Bug Fixes
232 | ---------
233 |
234 | - `#105 `__
235 | reverts a change that switched from `imp` to `importlib`. Using
236 | `importlib.find_spec()`
237 | is not safe at runtime as it can import modules which will cause
238 | side effects within environments.
239 |
240 | 7.0.0
241 | =====
242 |
243 | This major release drops support for Python 3.5. This version is not
244 | maintained anymore.
245 |
246 | Bug Fixes
247 | ---------
248 |
249 | - Fixes an issue with ellipsis incorrectly being interpreted as
250 | relative imports and triggering a `ValueError` in the
251 | `ImportableModuleFilter`. See `#96
252 | `__ for
253 | details.
254 |
255 | 6.0.0
256 | =====
257 |
258 | With this release, sphinxcontrib-spelling moves from beta to
259 | stable. It also updates the use of Python 3, including packaging
260 | metadata, code style, and test configuration.
261 |
262 | New Features
263 | ------------
264 |
265 | - Add packaging metadata declaring the project stable.
266 | - Add packaging metadata declaring support for Python 3 only.
267 | - Add packaging metadata indicating that this is a sphinx extension.
268 |
269 | Bug Fixes
270 | ---------
271 |
272 | - Replace use of deprecated `imp` module with `importlib`.
273 | - Update use of `pyenchant.get_tokenizer()` to pass filters argument
274 | as a keyword and avoid a runtime warning message.
275 | - Remove unused test dependency on `fixtures`.
276 | - Use `pyupgrade` to modernize the source code.
277 |
278 | 5.4.0
279 | =====
280 |
281 | New Features
282 | ------------
283 |
284 | - Added a new filter
285 | (``sphinxcontrib.spelling.filters.ContributorFilter``) that treats
286 | contributor names extracted from the git history as spelled
287 | correctly, making it easier to refer to the names in
288 | acknowledgments . Includes a new configuration option,
289 | ``spelling_ignore_contributor_names`` to enable it.
290 |
291 | 5.3.0
292 | =====
293 |
294 | New Features
295 | ------------
296 |
297 | - Add a configuration option ``spelling_exclude_patterns`` to manage
298 | skipping spell checking for some input files. The option uses a
299 | list of glob-style patterns that are matched against the source
300 | file names relative to the source directory. See :doc:`/customize`
301 | for more details. Contributed by sdelliot.
302 |
303 | 5.2.2
304 | =====
305 |
306 | Bug Fixes
307 | ---------
308 |
309 | - Updated to only create ``.spelling`` output files for inputs that
310 | generate spelling warnings. Fixes #63.
311 |
312 | 5.2.0
313 | =====
314 |
315 | New Features
316 | ------------
317 |
318 | - The builder is now registered using an entry point, so that if the
319 | ``spelling`` directive is not used in a project
320 | ``sphinxcontrib.spelling`` does not need to be included explicitly
321 | in the ``extensions`` list in ``conf.py`` in order to use it with
322 | the project on the command line.
323 |
324 | - PyEnchant is an optional dependency. If it is not installed, the
325 | spell checker will not work, but the extension can still be
326 | initialized. This allows projects that use spell checking to
327 | publish their documentation to ``readthedocs.org``, where it is
328 | not possible to install PyEnchant.
329 |
330 | - Restore support for parallel builds. Words that do not appear in
331 | any configured dictionary are written to a file named based on the
332 | input file, with the ``.rst`` extension replaced with
333 | ``.spelling``.
334 |
335 | 5.1.2
336 | =====
337 |
338 | - Mark as unsafe for parallel builds (contributed by Jared Dillard)
339 | - Add -W arg to sphinx-build in docs so warnings cause error
340 | (contributed by Elsa Gonsiorowski, PhD)
341 |
342 | 5.1.0
343 | =====
344 |
345 | - Add an option to show the line containing a misspelling for context
346 | (contributed by Huon Wilson)
347 |
348 | 5.0.0
349 | =====
350 |
351 | - Drop Python 2.7 support. (contributed by Johannes Raggam)
352 | - `allow customizing with classes using import strings
353 | `__
354 | - pyenchant is now maintained (contributed by Adam Johnson
355 |
356 | 4.3.0
357 | =====
358 |
359 | - Logging: use warning() instead of its deprecated alias (contributed
360 | by Sergey Kolosov)
361 | - Support additional contractions (contributed by David Baumgold)
362 | - require Sphinx >= 2.0.0
363 | - declare support for Python 3.6
364 |
365 | 4.2.1
366 | =====
367 |
368 | - fix remaining logging issue (contributed by Timotheus Kampik)
369 | - Remove usage of deprecated logging API (contributed by Tim Graham)
370 |
371 | 4.2.0
372 | =====
373 |
374 | - Fix a bug with empty word lists (contributed by FabioRosado)
375 | - Update dependency management to use setuptools extras
376 | - Document how to create multiple wordfiles (contributed by
377 | FabioRosado)
378 | - Note that PyEnchant is unmaintained and fix links (contributed by
379 | Marti Raudsepp)
380 | - Don’t use mutable default argument (contributed by Daniele Tricoli)
381 |
382 | 4.1.0
383 | =====
384 |
385 | - Make it possible to provide several wordlists (contributed by Tobias
386 | Olausson)
387 | - Update developer documentation (contributed by Tobias Olausson)
388 | - Update home page link (contributed by Devin Sevilla)
389 |
390 | 4.0.1
391 | =====
392 |
393 | - use the right method to emit warnings
394 | - disable smart quotes so that we can recognize
395 | contractions/possessives correctly (contributed by Alex Gaynor)
396 |
397 | 4.0.0
398 | =====
399 |
400 | - Don’t fail by default (contributed by Stephen Finucane)
401 | - Mark the extension as safe for parallel reading (contributed by Alex
402 | Gaynor)
403 | - be more verbose about configuration options
404 | - switch to testrepository for running tests
405 | - update Python 3.3 to 3.5
406 |
407 | 2.3.0
408 | =====
409 |
410 | - make it possible to specify tokenizer #7 (contributed by Timotheus
411 | Kampik)
412 |
413 | 2.2.0
414 | =====
415 |
416 | - Use ``https`` with ``pypi.python.org`` package name checker
417 | (contributed by John-Scott Atlakson)
418 | - Removed unnecessary shebang lines from non-script files (contributed
419 | by Avram Lubkin)
420 | - Re-enable the PyEnchant dependency (contributed by Julian Berman)
421 |
422 | 2.1.2
423 | =====
424 |
425 | - Fixed issue with six under Python 3.4
426 |
427 | 2.1.1
428 | =====
429 |
430 | - Use ``str.isupper()`` instead of ad-hoc method
431 | - fix syntax for tags directive
432 | - Removed no more used CHANGES file
433 |
434 | 2.1
435 | ===
436 |
437 | - Fix unicode error in ``PythonBuiltinsFilter``.
438 | - Make error output useful in emacs compiler mode
439 | - Only show the words being added to a local dictionary if debugging
440 | is enabled.
441 |
442 |
443 | 2.0
444 | ===
445 |
446 | - Add Python 3.3 support.
447 | - Add PyPy support.
448 | - Use pbr for packaging.
449 | - Update tox config to work with forked version of PyEnchant until
450 | changes are accepted upstream.
451 |
452 | 1.4
453 | ===
454 |
455 | - Fixed detection of builtins under PyPy, contributed by Hong Minhee
456 | (https://bitbucket.org/dahlia).
457 |
458 | 1.3
459 | ===
460 |
461 | - Handle text nodes without parents. (#19)
462 | - Include the input document name in the console output.
463 | - Use the Sphinx wrapper for registering a directive.
464 |
465 | 1.2
466 | ===
467 |
468 | - Add the document name to the messages showing the contents of a
469 | local dictionary created by the ``spelling`` directive.
470 | - Add title nodes to the list of node types checked for
471 | spelling. Resolves issue #17.
472 | - Add test/test_wordlist.txt to the manifest so it is included in
473 | the source distribution and the tests will pass. Resolves issue
474 | #17.
475 | - Documentation patch from Hank Gay.
476 |
477 | 1.1.1
478 | =====
479 |
480 | - Fix initialization so the per-document filters work even if no
481 | ``spelling`` directive is used.
482 |
483 | 1.1
484 | ===
485 |
486 | - Add an option treat the names of packages on PyPI as spelled
487 | properly.
488 | - Add an option to treat CamelCase names as spelled properly.
489 | - Add an option to treat acronyms as spelled properly.
490 | - Add an option to treat Python built-ins as spelled properly.
491 | - Add an option to treat names that can be found as modules as
492 | spelled properly.
493 | - Add an option to let the user provide a list of other filter
494 | classes for the tokenizer.
495 | - Add ``spelling`` directive for passing local configuration
496 | settings to the spelling checker. This version allows setting a
497 | list of words known to be spelled correctly.
498 |
499 | 1.0
500 | ===
501 |
502 | - Re-implement using just a Builder, without a separate visitor
503 | class.
504 | - Show the file and line number of any words not appearing in the
505 | dictionary, instead of the section title.
506 | - Log the file, line, and unknown words as the documents are
507 | processed.
508 |
509 | 0.2
510 | ===
511 |
512 | - Warn but otherwise ignore unknown node types.
513 |
514 | 0.1
515 | ===
516 |
517 | - First public release.
518 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. spelling:word-list::
2 |
3 | sphinxcontrib
4 |
5 | .. sphinxcontrib.spelling documentation master file, created by
6 | sphinx-quickstart on Sun Apr 17 15:33:23 2011.
7 | You can adapt this file completely to your liking, but it should at least
8 | contain the root `toctree` directive.
9 |
10 | ========================
11 | sphinxcontrib.spelling
12 | ========================
13 |
14 | ``sphinxcontrib.spelling`` is a spelling checker for Sphinx_. It uses
15 | PyEnchant_ to produce a report showing misspelled words.
16 |
17 | Features
18 | ========
19 |
20 | 1. Supports multiple source languages using the standard enchant
21 | dictionaries.
22 | 2. Supports project-specific dictionaries for localized jargon and
23 | other terminology that may not appear in the global dictionaries.
24 | 3. Suggests alternatives to words not found in the dictionary, when
25 | possible.
26 |
27 | Details
28 | =======
29 |
30 | .. toctree::
31 | :maxdepth: 2
32 |
33 | install
34 | customize
35 | run
36 | developers
37 | history
38 |
39 |
40 | .. _PyEnchant: https://github.com/rfk/pyenchant
41 |
42 | .. _Sphinx: https://www.sphinx-doc.org/
43 |
--------------------------------------------------------------------------------
/docs/source/install.rst:
--------------------------------------------------------------------------------
1 | .. spelling:word-list::
2 |
3 | sphinxcontrib
4 |
5 | ==============
6 | Installation
7 | ==============
8 |
9 | 1. Install the extension with pip: ``pip install sphinxcontrib-spelling``
10 |
11 | 2. Add ``'sphinxcontrib.spelling'`` to the ``extensions`` list in
12 | ``conf.py``.
13 |
14 | .. code-block:: python
15 |
16 | extensions = [ 'sphinxcontrib.spelling' ]
17 |
18 | 3. Then pass ``"spelling"`` as the builder argument to ``sphinx-build``.
19 |
20 | .. code-block:: shell-session
21 |
22 | $ sphinx-build -b spelling docs/source docs/build
23 |
--------------------------------------------------------------------------------
/docs/source/run.rst:
--------------------------------------------------------------------------------
1 | =========
2 | Running
3 | =========
4 |
5 | To process a document with the spell checker, use ``sphinx-build`` and
6 | specify ``spelling`` as the builder name using the ``-b`` option. The
7 | output includes the headings from the document and any misspelled
8 | words. If suggestions are enabled, they are shown on the same line as
9 | the misspelling. A log of the words in each input file not found in
10 | the dictionary is saved to the file ``.spelling`` under the
11 | build directory.
12 |
13 | .. code-block:: console
14 |
15 | $ tox -e spelling -r
16 | spelling create: .../sphinxcontrib-spelling/.tox/spelling
17 | spelling installdeps: .[docs]
18 | spelling develop-inst: .../sphinxcontrib-spelling
19 | 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
20 | spelling run-test-pre: PYTHONHASHSEED='1632297322'
21 | spelling run-test: commands[0] | sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling
22 | Running Sphinx v3.2.0
23 | Initializing Spelling Checker 5.2.1.dev2
24 | Ignoring wiki words
25 | Ignoring acronyms
26 | Adding package names from PyPI to local dictionary…
27 | Ignoring Python builtins
28 | Ignoring importable module names
29 | Adding contents of .../sphinxcontrib-spelling/docs/source/spelling_wordlist.txt to custom word list
30 | Adding contents of .../sphinxcontrib-spelling/docs/source/spelling_people.txt to custom word list
31 | Looking for custom word list in /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmphsetrn0s/spelling_wordlist.txt
32 | building [mo]: targets for 0 po files that are out of date
33 | building [spelling]: all documents
34 | updating environment: [new config] 6 added, 0 changed, 0 removed
35 | reading sources... [ 16%] customize
36 | reading sources... [ 33%] developers
37 | reading sources... [ 50%] history
38 | reading sources... [ 66%] index
39 | reading sources... [ 83%] install
40 | reading sources... [100%] run
41 |
42 | waiting for workers...
43 | scanning .../sphinxcontrib-spelling/releasenotes/notes for current branch release notes
44 | got versions ['5.2.0']
45 | looking for now-outdated files... none found
46 | pickling environment... done
47 | checking consistency... done
48 | preparing documents... done
49 | writing output... [ 16%] customize
50 | Extending local dictionary for customize
51 | writing output... [ 33%] developers
52 | Extending local dictionary for developers
53 | writing output... [ 50%] history
54 | Extending local dictionary for history
55 | writing output... [ 66%] index
56 | Extending local dictionary for index
57 | 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.
58 | Writing .../sphinxcontrib-spelling/docs/build/spelling/index.spelling
59 | writing output... [ 83%] install
60 | Extending local dictionary for install
61 | writing output... [100%] run
62 |
63 |
64 | Warning, treated as error:
65 | Found 1 misspelled words
66 | 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)
67 | __________________________________________________ summary ___________________________________________________
68 | ERROR: spelling: commands failed
69 |
--------------------------------------------------------------------------------
/docs/source/spelling_wordlist.txt:
--------------------------------------------------------------------------------
1 | builtins
2 | hoc
3 | linter
4 | linters
5 | macOS
6 | pypi
7 | reStructuredText
8 | sphinxcontrib
9 | tokenizer
10 | txt
11 | wikis
12 | wordfiles
13 | wordlist
14 | wordlists
15 |
--------------------------------------------------------------------------------
/integration_tests/build_django.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | """Try to build the Django documentation."""
4 |
5 | import argparse
6 | import os
7 | import subprocess
8 | import sys
9 | import tempfile
10 |
11 |
12 | def doit(*cmd, description="", cwd=None):
13 | print(f"\n[{description}]\nrunning: {' '.join(cmd)}")
14 | completed = subprocess.run(cmd, cwd=cwd)
15 | try:
16 | completed.check_returncode()
17 | except subprocess.CalledProcessError as err:
18 | raise RuntimeError(f"command failed {description}") from err
19 |
20 |
21 | def try_build(workdir, srcdir, django_repo):
22 | print(f"working in {workdir}")
23 | doit(
24 | "git",
25 | "clone",
26 | "--depth",
27 | "1",
28 | django_repo,
29 | "django",
30 | description="clone django",
31 | cwd=workdir,
32 | )
33 | djangodir = workdir + "/django"
34 | doit(
35 | "tox",
36 | "-e",
37 | "docs",
38 | "--notest",
39 | description="build django docs virtualenv",
40 | cwd=djangodir,
41 | )
42 | doit(
43 | ".tox/docs/bin/pip",
44 | "uninstall",
45 | "-y",
46 | "sphinxcontrib-spelling",
47 | description="uninstall packaged sphinxcontrib-spelling",
48 | cwd=djangodir,
49 | )
50 | doit(
51 | ".tox/docs/bin/pip",
52 | "install",
53 | srcdir,
54 | description="install sphinxcontrib-spelling from source",
55 | cwd=djangodir,
56 | )
57 | doit(
58 | "tox",
59 | "-e",
60 | "docs",
61 | description="build django docs",
62 | cwd=djangodir,
63 | )
64 |
65 |
66 | def main(args=sys.argv[1:]):
67 | parser = argparse.ArgumentParser()
68 | parser.add_argument(
69 | "--debug", action="store_true", default=False, help="show full tracebacks"
70 | )
71 | parser.add_argument("--src", help="source directory")
72 | parser.add_argument("--django-repo", default="https://github.com/django/django.git")
73 | parsed = parser.parse_args(args)
74 |
75 | srcdir = parsed.src
76 | if not srcdir:
77 | srcdir = os.path.realpath(os.path.dirname(os.path.dirname(sys.argv[0])))
78 |
79 | try:
80 | with tempfile.TemporaryDirectory() as dirname:
81 | try_build(dirname, srcdir, parsed.django_repo)
82 | except Exception as err:
83 | if parsed.debug:
84 | raise
85 | print(f"ERROR: {err}")
86 | return 1
87 | return 0
88 |
89 |
90 | if __name__ == "__main__":
91 | sys.exit(main())
92 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["hatchling", "hatch-vcs"]
3 | build-backend = "hatchling.build"
4 |
5 | [project]
6 | name = "sphinxcontrib-spelling"
7 | readme = "README.rst"
8 | authors = [{ name = "Doug Hellmann", email = "doug@doughellmann.com" }]
9 | description = "Sphinx spelling extension"
10 | dynamic = ["version"]
11 |
12 | classifiers = [
13 | "Development Status :: 5 - Production/Stable",
14 | "Environment :: Console",
15 | "Environment :: Web Environment",
16 | "Framework :: Sphinx :: Extension",
17 | "Intended Audience :: Developers",
18 | "License :: OSI Approved :: BSD License",
19 | "Operating System :: OS Independent",
20 | "Programming Language :: Python",
21 | "Programming Language :: Python :: 3",
22 | "Programming Language :: Python :: 3 :: Only",
23 | "Programming Language :: Python :: 3.10",
24 | "Programming Language :: Python :: 3.11",
25 | "Programming Language :: Python :: 3.12",
26 | "Programming Language :: Python :: Implementation :: CPython",
27 | "Topic :: Documentation",
28 | "Topic :: Utilities",
29 | ]
30 |
31 | requires-python = ">=3.10"
32 |
33 | dependencies = ["PyEnchant>=3.1.1", "Sphinx>=3.0.0", "requests>=2.32.3"]
34 |
35 | [project.optional-dependencies]
36 | test = ["pytest", "pytest-cov", "coverage!=4.4,>=4.0"]
37 |
38 | [project.entry-points."sphinx.builders"]
39 | spelling = "sphinxcontrib.spelling"
40 |
41 | [project.urls]
42 | homepage = "https://sphinxcontrib-spelling.readthedocs.io/en/latest/"
43 | repository = "https://github.com/sphinx-contrib/spelling"
44 |
45 | [tool.hatch.version]
46 | source = "vcs"
47 |
48 | [tool.hatch.build.hooks.vcs]
49 | version-file = "sphinxcontrib/spelling/version.py"
50 |
51 | [tool.hatch.build.targets.sdist]
52 | exclude = [".github", "cover", ".mergify.yml", ".gitignore"]
53 | [tool.hatch.build.targets.wheel]
54 | only-include = ["sphinxcontrib"]
55 |
56 | [tool.hatch.envs.docs]
57 | dependencies = ["sphinx"]
58 | [tool.hatch.envs.docs.env]
59 | ENABLE_SPELLING = "1"
60 | [tool.hatch.envs.docs.scripts]
61 | build = [
62 | "sphinx-build -W -j auto -b html -d docs/build/doctrees docs/source docs/build/html",
63 | "sphinx-build -W -j auto -b linkcheck -d docs/build/doctrees docs/source docs/build/linkcheck",
64 | "sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling",
65 | ]
66 | check = "sphinx-build -W -j auto -b spelling -d docs/build/doctrees docs/source docs/build/spelling"
67 |
68 | [tool.hatch.envs.test]
69 | dependencies = [
70 | "pytest",
71 | "pytest-cov",
72 | "coverage!=4.4,>=4.0",
73 | "ruff",
74 | "twine",
75 | "check-python-versions",
76 | ]
77 | [tool.hatch.envs.test.scripts]
78 | test = "python -m pytest --cov=sphinxcontrib.spelling --cov-report term-missing --log-level DEBUG tests"
79 | lint = [
80 | "ruff check sphinxcontrib integration_tests tests",
81 | "ruff format --check sphinxcontrib integration_tests tests",
82 | ]
83 | lint-fix = ["ruff format sphinxcontrib integration_tests tests"]
84 | pkglint = [
85 | "hatch build",
86 | "twine check dist/*.tar.gz dist/*.whl",
87 | "check-python-versions --only pyproject.toml,.github/workflows/test.yml",
88 | ]
89 |
90 | [tool.hatch.envs.integration]
91 | dependencies = ["tox"]
92 | [tool.hatch.envs.integration.scripts]
93 | django = "./integration_tests/build_django.py"
94 |
95 | [tool.ruff]
96 | exclude = ["sphinxcontrib/spelling/version.py"]
97 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | ruff
2 |
--------------------------------------------------------------------------------
/show-changelog.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | git_project=$(git remote get-url origin | cut -f2 -d: | sed 's/.git//')
4 | pr_url_base="https://github.com/${git_project}/pull/"
5 |
6 | git log --merges --pretty="format:- %s %b" $(git describe --abbrev=0).. \
7 | | sed -E \
8 | -e 's#Merge pull request ##g' \
9 | -e 's# from [^[:space:]]+##' \
10 | -e 's|#([[:digit:]]+)|`#\1 <'${pr_url_base}'\1>`__|g'
11 | echo
12 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | # For Python 3.8 and later
3 | import importlib.metadata as importlib_metadata
4 | except ImportError:
5 | # For everyone else
6 | import importlib_metadata
7 |
8 | from sphinx.util import logging
9 |
10 | from . import asset, builder, directive, domain
11 |
12 | logger = logging.getLogger(__name__)
13 |
14 |
15 | def setup(app):
16 | version = importlib_metadata.version("sphinxcontrib-spelling")
17 | logger.info("Initializing Spelling Checker %s", version)
18 | app.add_builder(builder.SpellingBuilder)
19 | # Register the 'spelling' directive for setting parameters within
20 | # a document
21 | app.add_directive("spelling", directive.LegacySpellingDirective)
22 | app.add_domain(domain.SpellingDomain)
23 | # Register an environment collector to merge data gathered by the
24 | # directive in parallel builds
25 | app.add_env_collector(asset.SpellingCollector)
26 | # Report guesses about correct spelling
27 | app.add_config_value("spelling_show_suggestions", False, "env")
28 | # Limit the number of suggestions output
29 | app.add_config_value("spelling_suggestion_limit", 0, "env")
30 | # Report the whole line that has the error
31 | app.add_config_value("spelling_show_whole_line", True, "env")
32 | # Emit misspelling as a sphinx warning instead of info message
33 | app.add_config_value("spelling_warning", False, "env")
34 | # Set the language for the text
35 | app.add_config_value("spelling_lang", "en_US", "env")
36 | # Set the language for the tokenizer
37 | app.add_config_value("tokenizer_lang", "en_US", "env")
38 | # Set a user-provided list of words known to be spelled properly
39 | app.add_config_value("spelling_word_list_filename", None, "env")
40 | # Assume anything that looks like a PyPI package name is spelled properly
41 | app.add_config_value("spelling_ignore_pypi_package_names", False, "env")
42 | # Assume words that look like wiki page names are spelled properly
43 | app.add_config_value("spelling_ignore_wiki_words", True, "env")
44 | # Assume words that are all caps, or all caps with trailing s, are
45 | # spelled properly
46 | app.add_config_value("spelling_ignore_acronyms", True, "env")
47 | # Assume words that are part of __builtins__ are spelled properly
48 | app.add_config_value("spelling_ignore_python_builtins", True, "env")
49 | # Assume words that look like the names of importable modules are
50 | # spelled properly
51 | app.add_config_value("spelling_ignore_importable_modules", True, "env")
52 | # Treat contributor names from git history as spelled correctly
53 | app.add_config_value("spelling_ignore_contributor_names", True, "env")
54 | # Add any user-defined filter classes
55 | app.add_config_value("spelling_filters", [], "env")
56 | # Set a user-provided list of files to ignore
57 | app.add_config_value("spelling_exclude_patterns", [], "env")
58 | # Choose whether or not the misspelled output should be displayed
59 | # in the terminal
60 | app.add_config_value("spelling_verbose", True, "env")
61 | return {
62 | "parallel_read_safe": True,
63 | "parallel_write_safe": True,
64 | "version": version,
65 | }
66 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/asset.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2020 Doug Hellmann. All rights reserved.
3 | #
4 | """Asset collector for additional spelling terms."""
5 |
6 | import collections
7 | import contextlib
8 |
9 | from sphinx.environment.collectors import EnvironmentCollector
10 | from sphinx.util import logging
11 |
12 | logger = logging.getLogger(__name__)
13 |
14 |
15 | class SpellingCollector(EnvironmentCollector):
16 | def clear_doc(self, app, env, docname) -> None:
17 | with contextlib.suppress(AttributeError, KeyError):
18 | del env.spelling_document_words[docname]
19 |
20 | def merge_other(self, app, env, docnames, other):
21 | try:
22 | other_words = other.spelling_document_words
23 | except AttributeError:
24 | other_words = {}
25 |
26 | if not hasattr(env, "spelling_document_words"):
27 | env.spelling_document_words = collections.defaultdict(list)
28 | env.spelling_document_words.update(other_words)
29 |
30 | def process_doc(self, app, doctree):
31 | pass
32 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/builder.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Spelling checker extension for Sphinx."""
5 |
6 | import collections
7 | import importlib
8 | import os
9 | import tempfile
10 |
11 | import docutils.nodes
12 | import docutils.utils
13 | from sphinx.builders import Builder
14 | from sphinx.util import logging, osutil
15 | from sphinx.util.console import red
16 | from sphinx.util.matching import Matcher
17 | from sphinx.util.osutil import ensuredir
18 |
19 | try:
20 | from enchant.tokenize import EmailFilter, WikiWordFilter
21 | except ImportError as imp_exc:
22 | enchant_import_error = imp_exc
23 | else:
24 | enchant_import_error = None
25 |
26 | from . import checker, filters
27 |
28 | logger = logging.getLogger(__name__)
29 |
30 | # TODO - Words with multiple uppercase letters treated as classes and ignored
31 |
32 |
33 | class SpellingBuilder(Builder):
34 | """
35 | Spell checks a document
36 | """
37 |
38 | name = "spelling"
39 |
40 | def init(self):
41 | if enchant_import_error is not None:
42 | raise RuntimeError(
43 | "Cannot initialize spelling builder without PyEnchant installed"
44 | ) from enchant_import_error
45 | self.misspelling_count = 0
46 |
47 | self.env.settings["smart_quotes"] = False
48 | # Initialize the per-document filters
49 | if not hasattr(self.env, "spelling_document_words"):
50 | self.env.spelling_document_words = collections.defaultdict(list)
51 |
52 | # Initialize the global filters
53 | f = [
54 | filters.ContractionFilter,
55 | EmailFilter,
56 | ]
57 | if self.config.spelling_ignore_wiki_words:
58 | logger.info("Ignoring wiki words")
59 | f.append(WikiWordFilter)
60 | if self.config.spelling_ignore_acronyms:
61 | logger.info("Ignoring acronyms")
62 | f.append(filters.AcronymFilter)
63 | if self.config.spelling_ignore_pypi_package_names:
64 | logger.info("Adding package names from PyPI to local dictionary…")
65 | f.append(filters.PyPIFilterFactory())
66 | if self.config.spelling_ignore_python_builtins:
67 | logger.info("Ignoring Python builtins")
68 | f.append(filters.PythonBuiltinsFilter)
69 | if self.config.spelling_ignore_importable_modules:
70 | logger.info("Ignoring importable module names")
71 | f.append(filters.ImportableModuleFilter)
72 | if self.config.spelling_ignore_contributor_names:
73 | logger.info("Ignoring contributor names")
74 | f.append(filters.ContributorFilter)
75 | f.extend(self._load_filter_classes(self.config.spelling_filters))
76 |
77 | if not os.path.isdir(self.outdir):
78 | os.mkdir(self.outdir)
79 |
80 | word_list = self.get_wordlist_filename()
81 | logger.info("Looking for custom word list in %s", word_list)
82 |
83 | self.checker = checker.SpellingChecker(
84 | lang=self.config.spelling_lang,
85 | tokenizer_lang=self.config.tokenizer_lang,
86 | suggest=self.config.spelling_show_suggestions,
87 | word_list_filename=word_list,
88 | filters=f,
89 | context_line=self.config.spelling_show_whole_line,
90 | )
91 |
92 | def _load_filter_classes(self, filters):
93 | # Filters may be expressed in the configuration file using
94 | # names, so look through them and import the referenced class
95 | # and use that in the checker.
96 | for filter_ in filters:
97 | if not isinstance(filter_, str):
98 | yield filter_
99 | continue
100 | module_name, _, class_name = filter_.rpartition(".")
101 | mod = importlib.import_module(module_name)
102 | yield getattr(mod, class_name)
103 |
104 | def get_configured_wordlist_filenames(self):
105 | "Returns the configured wordlist filenames."
106 | word_list = self.config.spelling_word_list_filename
107 | if word_list is None:
108 | word_list = ["spelling_wordlist.txt"]
109 |
110 | if isinstance(word_list, str):
111 | # Wordlist is a string. Split on comma in case it came
112 | # from the command line, via -D, and has multiple values.
113 | word_list = word_list.split(",")
114 |
115 | return [os.path.join(self.srcdir, p) for p in word_list]
116 |
117 | def get_wordlist_filename(self):
118 | "Returns the filename of the wordlist to use when checking content."
119 | filenames = self.get_configured_wordlist_filenames()
120 | if len(filenames) == 1:
121 | return filenames[0]
122 | # In case the user has multiple word lists, we combine them
123 | # into one large list that we pass on to the checker.
124 | return self._build_combined_wordlist()
125 |
126 | def _build_combined_wordlist(self):
127 | # If we have a list, the combined list is the first list plus all words
128 | # from the other lists. Otherwise, word_list is assumed to just be a
129 | # string.
130 | temp_dir = tempfile.mkdtemp()
131 | combined_word_list = os.path.join(temp_dir, "spelling_wordlist.txt")
132 |
133 | with open(combined_word_list, "w", encoding="UTF-8") as outfile:
134 | for word_file in self.get_configured_wordlist_filenames():
135 | # Paths are relative
136 | long_word_file = os.path.join(self.srcdir, word_file)
137 | logger.info("Adding contents of %s to custom word list", long_word_file)
138 | with open(long_word_file, encoding="UTF-8") as infile:
139 | infile_contents = infile.readlines()
140 | outfile.writelines(infile_contents)
141 |
142 | # Check for newline, and add one if not present
143 | if infile_contents and not infile_contents[-1].endswith("\n"):
144 | outfile.write("\n")
145 |
146 | return combined_word_list
147 |
148 | def get_outdated_docs(self):
149 | return "all documents"
150 |
151 | def prepare_writing(self, docnames):
152 | return
153 |
154 | def get_target_uri(self, docname, typ=None):
155 | return ""
156 |
157 | def get_suggestions_to_show(self, suggestions):
158 | if not self.config.spelling_show_suggestions or not suggestions:
159 | return []
160 | to_show = suggestions
161 | try:
162 | n_to_show = int(self.config.spelling_suggestion_limit)
163 | except ValueError:
164 | n_to_show = 0
165 | if n_to_show > 0:
166 | to_show = suggestions[:n_to_show]
167 | return to_show
168 |
169 | def format_suggestions(self, suggestions):
170 | to_show = self.get_suggestions_to_show(suggestions)
171 | if not to_show:
172 | return ""
173 | return "[" + ", ".join('"%s"' % s for s in to_show) + "]"
174 |
175 | TEXT_NODES = {
176 | "block_quote",
177 | "caption",
178 | "paragraph",
179 | "list_item",
180 | "term",
181 | "definition_list_item",
182 | "title",
183 | }
184 |
185 | def write_doc(self, docname, doctree):
186 | lines = list(self._find_misspellings(docname, doctree))
187 | self.misspelling_count += len(lines)
188 | if lines:
189 | output_filename = os.path.join(self.outdir, f"{docname}.spelling")
190 | logger.info("Writing %s", output_filename)
191 | ensuredir(os.path.dirname(output_filename))
192 | with open(output_filename, "w", encoding="UTF-8") as output:
193 | output.writelines(lines)
194 |
195 | def _find_misspellings(self, docname, doctree):
196 | excluded = Matcher(self.config.spelling_exclude_patterns)
197 | if excluded(self.env.doc2path(docname, None)):
198 | return
199 | # Build the document-specific word filter based on any good
200 | # words listed in spelling directives. If we have no such
201 | # words, we want to push an empty list of filters so that we
202 | # can always safely pop the filter stack when we are done with
203 | # this document.
204 | doc_filters = []
205 | good_words = self.env.spelling_document_words.get(docname)
206 | if good_words:
207 | logger.debug("Extending local dictionary for %s", docname)
208 | doc_filters.append(filters.IgnoreWordsFilterFactory(good_words))
209 | self.checker.push_filters(doc_filters)
210 |
211 | # Set up a filter for the types of nodes to ignore during
212 | # traversal.
213 | def filter(n):
214 | if n.tagname != "#text":
215 | return False
216 | if n.parent and n.parent.tagname not in self.TEXT_NODES:
217 | return False
218 | # Nodes marked by the spelling:ignore role
219 | if hasattr(n, "spellingIgnore"):
220 | return False
221 | return True
222 |
223 | for node in doctree.findall(filter):
224 | # Get the location of the text being checked so we can
225 | # report it in the output file. Nodes from text that
226 | # comes in via an 'include' directive does not include
227 | # the full path, so convert all to relative path
228 | # for consistency.
229 | source, node_lineno = docutils.utils.get_source_line(node)
230 | source = osutil.relpath(source)
231 |
232 | # Check the text of the node.
233 | misspellings = self.checker.check(node.astext())
234 | for word, suggestions, context_line, line_offset in misspellings:
235 | # Avoid TypeError on nodes lacking a line number
236 | # This happens for some node originating from docstrings
237 | lineno = node_lineno
238 | if lineno is not None:
239 | lineno += line_offset
240 |
241 | msg_parts = [
242 | f"{source}:{lineno}: ",
243 | "Spell check",
244 | red(word),
245 | ]
246 | if self.format_suggestions(suggestions) != "":
247 | msg_parts.append(self.format_suggestions(suggestions))
248 | msg_parts.append(context_line)
249 | msg = ": ".join(msg_parts) + "."
250 | if self.config.spelling_warning:
251 | logger.warning(msg)
252 | elif self.config.spelling_verbose:
253 | logger.info(msg)
254 | yield "%s:%s: (%s) %s %s\n" % (
255 | source,
256 | lineno,
257 | word,
258 | self.format_suggestions(suggestions),
259 | context_line,
260 | )
261 |
262 | self.checker.pop_filters()
263 | return
264 |
265 | def finish(self):
266 | if self.misspelling_count:
267 | logger.warning("Found %d misspelled words", self.misspelling_count)
268 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/checker.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Spelling checker extension for Sphinx."""
5 |
6 | try:
7 | import enchant
8 | from enchant.tokenize import get_tokenizer
9 | except ImportError as imp_exc:
10 | enchant_import_error = imp_exc
11 | else:
12 | enchant_import_error = None
13 |
14 |
15 | class SpellingChecker:
16 | """Checks the spelling of blocks of text.
17 |
18 | Uses options defined in the sphinx configuration file to control
19 | the checking and filtering behavior.
20 | """
21 |
22 | def __init__(
23 | self,
24 | lang,
25 | suggest,
26 | word_list_filename,
27 | tokenizer_lang="en_US",
28 | filters=None,
29 | context_line=False,
30 | ):
31 | if enchant_import_error is not None:
32 | raise RuntimeError(
33 | "Cannot instantiate SpellingChecker without PyEnchant installed",
34 | ) from enchant_import_error
35 | if filters is None:
36 | filters = []
37 | self.dictionary = enchant.DictWithPWL(lang, word_list_filename)
38 | self.tokenizer = get_tokenizer(tokenizer_lang, filters=filters)
39 | self.original_tokenizer = self.tokenizer
40 | self.suggest = suggest
41 | self.context_line = context_line
42 |
43 | def push_filters(self, new_filters):
44 | """Add a filter to the tokenizer chain."""
45 | t = self.tokenizer
46 | for f in new_filters:
47 | t = f(t)
48 | self.tokenizer = t
49 |
50 | def pop_filters(self):
51 | """Remove the filters pushed during the last call to push_filters()."""
52 | self.tokenizer = self.original_tokenizer
53 |
54 | def check(self, text):
55 | """Yields bad words and suggested alternate spellings."""
56 | for word, pos in self.tokenizer(text):
57 | correct = self.dictionary.check(word)
58 | if correct:
59 | continue
60 |
61 | suggestions = self.dictionary.suggest(word) if self.suggest else []
62 | line = line_of_index(text, pos) if self.context_line else ""
63 | line_offset = text.count("\n", 0, pos)
64 |
65 | yield word, suggestions, line, line_offset
66 |
67 |
68 | def line_of_index(text, index):
69 | try:
70 | line_start = text.rindex("\n", 0, index) + 1
71 | except ValueError:
72 | line_start = 0
73 | try:
74 | line_end = text.index("\n", index)
75 | except ValueError:
76 | line_end = len(text)
77 |
78 | return text[line_start:line_end]
79 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/directive.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Spelling checker extension for Sphinx."""
5 |
6 | import collections
7 |
8 | from docutils.parsers import rst
9 | from sphinx.util import logging
10 |
11 | logger = logging.getLogger(__name__)
12 |
13 |
14 | def add_good_words_to_document(env, docname, good_words):
15 | # Initialize the per-document good words list
16 | if not hasattr(env, "spelling_document_words"):
17 | env.spelling_document_words = collections.defaultdict(list)
18 | logger.debug("Extending local dictionary for %s with %s", env.docname, good_words)
19 | env.spelling_document_words[env.docname].extend(good_words)
20 |
21 |
22 | class SpellingDirective(rst.Directive):
23 | """Custom directive for passing instructions to the spelling checker.
24 |
25 | .. spelling::
26 |
27 | word1
28 | word2
29 |
30 | """
31 |
32 | has_content = True
33 |
34 | def run(self):
35 | env = self.state.document.settings.env
36 |
37 | good_words = []
38 | for entry in self.content:
39 | if not entry:
40 | continue
41 | good_words.extend(entry.split())
42 | if good_words:
43 | add_good_words_to_document(env, env.docname, good_words)
44 |
45 | return []
46 |
47 |
48 | class LegacySpellingDirective(SpellingDirective):
49 | def run(self):
50 | logger.info(
51 | "direct use of the spelling directive is deprecated, "
52 | 'replace ".. spelling::" with ".. spelling:word-list::"'
53 | )
54 | return super().run()
55 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/domain.py:
--------------------------------------------------------------------------------
1 | from sphinx.domains import Domain
2 |
3 | from . import directive, role
4 |
5 |
6 | class SpellingDomain(Domain):
7 | name = "spelling"
8 | label = "Spelling Checker"
9 | directives = {
10 | "word-list": directive.SpellingDirective,
11 | }
12 | roles = {"word": role.spelling_word, "ignore": role.spelling_ignore}
13 |
14 | def get_objects(self):
15 | return []
16 |
17 | def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
18 | return None
19 |
20 | def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
21 | return []
22 |
23 | def merge_domaindata(self, docnames, otherdata):
24 | pass
25 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/filters.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Spelling checker extension for Sphinx."""
5 |
6 | # TODO - Words with multiple uppercase letters treated as classes and ignored
7 |
8 | import builtins
9 | import importlib
10 | import subprocess
11 | import sys
12 |
13 | import requests
14 | from enchant.tokenize import Filter, get_tokenizer, tokenize, unit_tokenize
15 | from sphinx.util import logging
16 |
17 | logger = logging.getLogger(__name__)
18 |
19 |
20 | class AcronymFilter(Filter):
21 | """If a word looks like an acronym (all upper case letters),
22 | ignore it.
23 | """
24 |
25 | def _skip(self, word):
26 | return (
27 | word.isupper() # all caps
28 | or
29 | # pluralized acronym ("URLs")
30 | (word[-1].lower() == "s" and word[:-1].isupper())
31 | )
32 |
33 |
34 | class list_tokenize(tokenize):
35 | def __init__(self, words):
36 | super().__init__("")
37 | self._words = words
38 |
39 | def next(self):
40 | if not self._words:
41 | raise StopIteration()
42 | word = self._words.pop(0)
43 | return (word, 0)
44 |
45 |
46 | class ContractionFilter(Filter):
47 | """Strip common contractions from words."""
48 |
49 | splits = {
50 | "aren't": ["are", "not"],
51 | "can't": ["can", "not"],
52 | "could've": ["could", "have"],
53 | "couldn't": ["could", "not"],
54 | "didn't": ["did", "not"],
55 | "doesn't": ["does", "not"],
56 | "don't": ["do", "not"],
57 | "hadn't": ["had", "not"],
58 | "hasn't": ["has", "not"],
59 | "haven't": ["have", "not"],
60 | "he'd": ["he", "would"],
61 | "he'll": ["he", "will"],
62 | "he's": ["he", "is"],
63 | "how'd": ["how", "would"],
64 | "how'll": ["how", "will"],
65 | "how's": ["how", "is"],
66 | "i'd": ["I", "would"],
67 | "i'll": ["I", "will"],
68 | "i'm": ["I", "am"],
69 | "i've": ["I", "have"],
70 | "isn't": ["is", "not"],
71 | "it'd": ["it", "would"],
72 | "it'll": ["it", "will"],
73 | "it's": ["it", "is"],
74 | "ma'am": ["madam"],
75 | "might've": ["might", "have"],
76 | "mightn't": ["might", "not"],
77 | "must've": ["must", "have"],
78 | "mustn't": ["must", "not"],
79 | "o'": ["of"],
80 | "o'clock": ["of", "the", "clock"],
81 | "she'd": ["she", "would"],
82 | "she'll": ["she", "will"],
83 | "she's": ["she", "is"],
84 | "should've": ["should", "have"],
85 | "shouldn't": ["should", "not"],
86 | "that'd": ["that", "would"],
87 | "that'll": ["that", "will"],
88 | "that's": ["that", "is"],
89 | "they'd": ["they", "would"],
90 | "they'll": ["they", "will"],
91 | "they're": ["they", "are"],
92 | "they've": ["they", "have"],
93 | "wasn't": ["was", "not"],
94 | "we'd": ["we", "would"],
95 | "we'll": ["we", "will"],
96 | "we're": ["we", "are"],
97 | "we've": ["we", "have"],
98 | "weren't": ["were", "not"],
99 | "what'd": ["what", "would"],
100 | "what'll": ["what", "will"],
101 | "what're": ["what", "are"],
102 | "what's": ["what", "is"],
103 | "when'd": ["when", "would"],
104 | "when'll": ["when", "will"],
105 | "when's": ["when", "is"],
106 | "where'd": ["where", "would"],
107 | "where'll": ["where", "will"],
108 | "where's": ["where", "is"],
109 | "who'd": ["who", "would"],
110 | "who'll": ["who", "will"],
111 | "who's": ["who", "is"],
112 | "why'd": ["why", "would"],
113 | "why'll": ["why", "will"],
114 | "why's": ["why", "is"],
115 | "won't": ["will", "not"],
116 | "would've": ["would", "have"],
117 | "wouldn't": ["would", "not"],
118 | "you'd": ["you", "would"],
119 | "you'll": ["you", "will"],
120 | "you're": ["you", "are"],
121 | "you've": ["you", "have"],
122 | }
123 |
124 | def _split(self, word):
125 | # Fixed responses
126 | if word.lower() in self.splits:
127 | return list_tokenize(self.splits[word.lower()])
128 |
129 | # Possessive
130 | if word.lower().endswith("'s"):
131 | return unit_tokenize(word[:-2])
132 |
133 | # * not
134 | if word.lower().endswith("n't"):
135 | return unit_tokenize(word[:-3])
136 |
137 | return unit_tokenize(word)
138 |
139 |
140 | class IgnoreWordsFilter(Filter):
141 | """Given a set of words, ignore them all."""
142 |
143 | def __init__(self, tokenizer, word_set):
144 | self.word_set = set(word_set)
145 | super().__init__(tokenizer)
146 |
147 | def _skip(self, word):
148 | return word in self.word_set
149 |
150 |
151 | class IgnoreWordsFilterFactory:
152 | def __init__(self, words):
153 | self.words = words
154 |
155 | def __call__(self, tokenizer):
156 | return IgnoreWordsFilter(tokenizer, self.words)
157 |
158 |
159 | class PyPIFilterFactory(IgnoreWordsFilterFactory):
160 | """Build an IgnoreWordsFilter for all of the names of packages on PyPI."""
161 |
162 | def __init__(self):
163 | r = requests.get(
164 | "https://pypi.org/simple/",
165 | headers={
166 | "user-agent": "sphinxcontrib.spelling",
167 | "accept": "application/vnd.pypi.simple.v1+json",
168 | },
169 | )
170 | names = [i["name"] for i in r.json()["projects"]]
171 | logger.debug("retrieved %d project names from pypi.org", len(names))
172 | super().__init__(names)
173 |
174 |
175 | class PythonBuiltinsFilter(Filter):
176 | """Ignore names of built-in Python symbols."""
177 |
178 | def _skip(self, word):
179 | return hasattr(builtins, word)
180 |
181 |
182 | class ImportableModuleFilter(Filter):
183 | """Ignore names of modules that we could import."""
184 |
185 | def __init__(self, tokenizer):
186 | super().__init__(tokenizer)
187 | self.found_modules = set(sys.builtin_module_names)
188 | self.sought_modules = self.found_modules.copy()
189 | # By adding __main__ to the list of sought modules but not
190 | # found modules we ensure that it is never recognized as a
191 | # valid module, which is consistent with the behavior before
192 | # version 7.3.1. See
193 | # https://github.com/sphinx-contrib/spelling/issues/141
194 | self.sought_modules.add("__main__")
195 |
196 | def _skip(self, word):
197 | # If the word looks like a python module filename, strip the
198 | # extension to avoid the side-effect of actually importing the
199 | # module. This prevents, for example, 'setup.py' triggering an
200 | # import of the setup module during a doc build, which makes
201 | # it look like Sphinx is complaining about a commandline
202 | # argument. See
203 | # https://github.com/sphinx-contrib/spelling/issues/142
204 | if word.endswith(".py"):
205 | logger.debug(
206 | "removing .py extension from %r before searching for module", word
207 | )
208 | word = word[:-3]
209 |
210 | valid_module_name = all(n.isidentifier() for n in word.split("."))
211 | if not valid_module_name:
212 | return False
213 |
214 | if word not in self.sought_modules:
215 | self.sought_modules.add(word)
216 | try:
217 | mod = importlib.util.find_spec(word)
218 | except BaseException as err:
219 | # This could be an ImportError, SystemExit, some more detailed
220 | # error out of distutils, or something else triggered
221 | # by failing to be able to import a parent package to
222 | # use the metadata to search for a subpackage.
223 | logger.debug("find_spec(%r) failed, invalid module name: %s", word, err)
224 | else:
225 | if mod is not None:
226 | self.found_modules.add(word)
227 |
228 | return word in self.found_modules
229 |
230 |
231 | class ContributorFilter(IgnoreWordsFilter):
232 | """Accept information about contributors as spelled correctly.
233 |
234 | Look in the git history for authors and committers and accept
235 | tokens that are in the set.
236 | """
237 |
238 | _pretty_format = "%(trailers:key=Co-Authored-By,separator=%x0A)%x0A%an%x0A%cn"
239 |
240 | def __init__(self, tokenizer):
241 | contributors = self._get_contributors()
242 | super().__init__(tokenizer, contributors)
243 |
244 | def _get_contributors(self):
245 | logger.info("Scanning contributors")
246 | cmd = [
247 | "git",
248 | "log",
249 | "--quiet",
250 | "--no-color",
251 | f"--pretty=format:{self._pretty_format}",
252 | ]
253 |
254 | try:
255 | p = subprocess.run(cmd, check=True, stdout=subprocess.PIPE)
256 | except (subprocess.CalledProcessError, FileNotFoundError) as err:
257 | logger.warning("Called: %s", " ".join(cmd))
258 | logger.warning("Failed to scan contributors: %s", err)
259 | return set()
260 | output = p.stdout.decode("utf-8")
261 | tokenizer = get_tokenizer("en_US", filters=[])
262 | return {word for word, pos in tokenizer(output)}
263 |
--------------------------------------------------------------------------------
/sphinxcontrib/spelling/role.py:
--------------------------------------------------------------------------------
1 | from docutils import nodes
2 |
3 | from . import directive
4 |
5 |
6 | def spelling_word(role, rawtext, text, lineno, inliner, options={}, content=[]):
7 | """Let the user indicate that inline text is spelled correctly."""
8 | env = inliner.document.settings.env
9 | docname = env.docname
10 | good_words = text.split()
11 | directive.add_good_words_to_document(env, docname, good_words)
12 | node = nodes.Text(text)
13 | return [node], []
14 |
15 |
16 | def spelling_ignore(role, rawtext, text, lineno, inliner, options={}, content=[]):
17 | """Let the user indicate that inline text is to not be spellchecked."""
18 | node = nodes.Text(text)
19 | setattr(node, "spellingIgnore", True)
20 | return [node], []
21 |
--------------------------------------------------------------------------------
/tests/helpers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import pathlib
5 |
6 | import pytest
7 |
8 |
9 | def require_git_repo(f):
10 | return pytest.mark.skipif(
11 | not (pathlib.Path(os.getcwd()) / ".git").is_dir(), reason="Not a git repo"
12 | )(f)
13 |
--------------------------------------------------------------------------------
/tests/test_builder.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Tests for SpellingBuilder"""
5 |
6 | import contextlib
7 | import io
8 | import os
9 | import sys
10 | import textwrap
11 |
12 | import pytest
13 | from sphinx.application import Sphinx
14 |
15 | from tests import helpers # isort:skip
16 |
17 |
18 | def _make_sphinx_project(tmpdir):
19 | srcdir = tmpdir.mkdir("src")
20 | outdir = tmpdir.mkdir("out")
21 | add_file(
22 | srcdir,
23 | "conf.py",
24 | """
25 | extensions = [ 'sphinxcontrib.spelling' ]
26 | """,
27 | )
28 | return (srcdir, outdir)
29 |
30 |
31 | @pytest.fixture
32 | def sphinx_project(tmpdir):
33 | yield _make_sphinx_project(tmpdir)
34 |
35 |
36 | @contextlib.contextmanager
37 | def working_dir(targetdir):
38 | "Temporarily change the working directory of the process."
39 | before = os.getcwd()
40 | os.chdir(targetdir)
41 | try:
42 | yield os.getcwd()
43 | finally:
44 | os.chdir(before)
45 |
46 |
47 | @contextlib.contextmanager
48 | def import_path(new_path):
49 | "Temporarily change sys.path for imports."
50 | before = sys.path
51 | try:
52 | sys.path = new_path
53 | yield
54 | finally:
55 | sys.path = before
56 |
57 |
58 | def add_file(thedir, filename, content):
59 | with open(thedir.join(filename), "w") as f:
60 | f.write(textwrap.dedent(content))
61 |
62 |
63 | def get_sphinx_app(srcdir, outdir, docname, builder="spelling"):
64 | stdout = io.StringIO()
65 | stderr = io.StringIO()
66 | app = Sphinx(
67 | srcdir,
68 | srcdir,
69 | outdir,
70 | outdir,
71 | builder,
72 | status=stdout,
73 | warning=stderr,
74 | freshenv=True,
75 | )
76 | return (stdout, stderr, app)
77 |
78 |
79 | def get_sphinx_output(srcdir, outdir, docname, builder="spelling"):
80 | stdout, stderr, app = get_sphinx_app(srcdir, outdir, docname, builder)
81 | app.build()
82 | path = os.path.join(outdir, f"{docname}.spelling")
83 | try:
84 | with open(path, "r") as f:
85 | output_text = f.read()
86 | except FileNotFoundError:
87 | output_text = None
88 | return (stdout, stderr, output_text)
89 |
90 |
91 | def test_setup(sphinx_project):
92 | srcdir, outdir = sphinx_project
93 | stdout = io.StringIO()
94 | stderr = io.StringIO()
95 | # If the spelling builder is not properly initialized,
96 | # trying to use it with the Sphinx app class will
97 | # generate an exception.
98 | Sphinx(
99 | str(srcdir),
100 | str(srcdir),
101 | str(outdir),
102 | str(outdir),
103 | "spelling",
104 | status=stdout,
105 | warning=stderr,
106 | freshenv=True,
107 | )
108 |
109 |
110 | def test_title(sphinx_project):
111 | srcdir, outdir = sphinx_project
112 | add_file(
113 | srcdir,
114 | "contents.rst",
115 | """
116 | Welcome to Speeling Checker documentation!
117 | ==========================================
118 | """,
119 | )
120 | stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, "contents")
121 | assert "(Speeling)" in output_text
122 |
123 |
124 | def test_body(sphinx_project):
125 | srcdir, outdir = sphinx_project
126 | add_file(
127 | srcdir,
128 | "contents.rst",
129 | """
130 | Welcome to Spelling Checker documentation!
131 | ==========================================
132 |
133 | There are several mispelled words in this txt.
134 | """,
135 | )
136 | stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, "contents")
137 | assert "(mispelled)" in output_text
138 | assert "(txt)" in output_text
139 |
140 |
141 | def test_ignore_literals(sphinx_project):
142 | srcdir, outdir = sphinx_project
143 | add_file(
144 | srcdir,
145 | "contents.rst",
146 | """
147 | Welcome to Spelling Checker documentation!
148 | ==========================================
149 |
150 | There are several misspelled words in this text.
151 |
152 | ::
153 |
154 | Literal blocks are ignoreed.
155 |
156 | Inline ``litterals`` are ignored, too.
157 |
158 | """,
159 | )
160 | stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, "contents")
161 | # The 'contents.spelling' output file should not have been
162 | # created, because the errors are ignored.
163 | assert output_text is None
164 |
165 |
166 | def test_several_word_lists(sphinx_project):
167 | srcdir, outdir = sphinx_project
168 | add_file(
169 | srcdir,
170 | "conf.py",
171 | """
172 | extensions = ['sphinxcontrib.spelling']
173 | spelling_word_list_filename=['test_wordlist.txt','test_wordlist2.txt']
174 | """,
175 | )
176 |
177 | add_file(
178 | srcdir,
179 | "contents.rst",
180 | """
181 | Welcome to Spelling Checker documentation!
182 | ==========================================
183 |
184 | There are several mispelled words in tihs txt.
185 | """,
186 | )
187 |
188 | add_file(
189 | srcdir,
190 | "test_wordlist.txt",
191 | """
192 | txt
193 | """,
194 | )
195 |
196 | add_file(
197 | srcdir,
198 | "test_wordlist2.txt",
199 | """
200 | mispelled
201 | """,
202 | )
203 | stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, "contents")
204 | # Both of these should be fine now
205 | assert "(mispelled)" not in output_text
206 | assert "(txt)" not in output_text
207 | # But not this one
208 | assert "(tihs)" in output_text
209 |
210 |
211 | def _wordlist_sphinx_project(tmpdir, conf_contents):
212 | srcdir, outdir = _make_sphinx_project(tmpdir)
213 | add_file(srcdir, "conf.py", conf_contents)
214 | add_file(
215 | srcdir,
216 | "test_wordlist.txt",
217 | """
218 | txt
219 | """,
220 | )
221 | add_file(
222 | srcdir,
223 | "test_wordlist2.txt",
224 | """
225 | mispelled
226 | """,
227 | )
228 | stdout, stderr, app = get_sphinx_app(srcdir, outdir, "contents")
229 | return (srcdir, outdir, stdout, stderr, app)
230 |
231 |
232 | def test_word_list_default(tmpdir):
233 | srcdir, outdir, stdout, stderr, app = _wordlist_sphinx_project(
234 | tmpdir,
235 | """
236 | extensions = ['sphinxcontrib.spelling']
237 | """,
238 | )
239 | results = app.builder.get_configured_wordlist_filenames()
240 | assert len(results) == 1
241 | assert os.path.basename(results[0]) == "spelling_wordlist.txt"
242 |
243 |
244 | def test_one_word_list_str(tmpdir):
245 | srcdir, outdir, stdout, stderr, app = _wordlist_sphinx_project(
246 | tmpdir,
247 | """
248 | extensions = ['sphinxcontrib.spelling']
249 | spelling_word_list_filename='test_wordlist.txt'
250 | """,
251 | )
252 | results = app.builder.get_configured_wordlist_filenames()
253 | assert len(results) == 1
254 | assert os.path.basename(results[0]) == "test_wordlist.txt"
255 |
256 |
257 | def test_multiple_word_list_str(tmpdir):
258 | # We don't expect anyone to set up their conf.py this way but it
259 | # simulates passing the configuration option from the command line
260 | # using -D.
261 | srcdir, outdir, stdout, stderr, app = _wordlist_sphinx_project(
262 | tmpdir,
263 | """
264 | extensions = ['sphinxcontrib.spelling']
265 | spelling_word_list_filename='test_wordlist.txt,test_wordlist2.txt'
266 | """,
267 | )
268 | results = app.builder.get_configured_wordlist_filenames()
269 | assert len(results) == 2
270 | assert os.path.basename(results[0]) == "test_wordlist.txt"
271 | assert os.path.basename(results[1]) == "test_wordlist2.txt"
272 |
273 |
274 | def test_multiple_word_list_list(tmpdir):
275 | srcdir, outdir, stdout, stderr, app = _wordlist_sphinx_project(
276 | tmpdir,
277 | """
278 | extensions = ['sphinxcontrib.spelling']
279 | spelling_word_list_filename=['test_wordlist.txt', 'test_wordlist2.txt']
280 | """,
281 | )
282 | results = app.builder.get_configured_wordlist_filenames()
283 | assert len(results) == 2
284 | assert os.path.basename(results[0]) == "test_wordlist.txt"
285 | assert os.path.basename(results[1]) == "test_wordlist2.txt"
286 |
287 |
288 | def test_ignore_file(sphinx_project):
289 | srcdir, outdir = sphinx_project
290 | add_file(
291 | srcdir,
292 | "conf.py",
293 | """
294 | extensions = ['sphinxcontrib.spelling']
295 | spelling_exclude_patterns=['con*']
296 | """,
297 | )
298 |
299 | add_file(
300 | srcdir,
301 | "contents.rst",
302 | """
303 | Welcome to Speeling Checker documentation!
304 | ==========================================
305 | """,
306 | )
307 |
308 | stdout, stderr, output_text = get_sphinx_output(srcdir, outdir, "contents")
309 | # The 'contents.spelling' output file should not have been
310 | # created, because the file is ignored.
311 | assert output_text is None
312 |
313 |
314 | @helpers.require_git_repo
315 | def test_docstrings(sphinx_project):
316 | srcdir, outdir = sphinx_project
317 |
318 | add_file(
319 | srcdir,
320 | "conf.py",
321 | """
322 | extensions = ['sphinxcontrib.spelling', 'sphinx.ext.autodoc']
323 | """,
324 | )
325 |
326 | add_file(
327 | srcdir / "..",
328 | "the_source.py",
329 | '''
330 | #!/usr/bin/env python3
331 |
332 | def public_function(arg_name):
333 | """Does something useful.
334 |
335 | :param arg_name: Pass a vaule
336 | """
337 | return 1
338 | ''',
339 | )
340 |
341 | add_file(
342 | srcdir,
343 | "contents.rst",
344 | """
345 | The Module
346 | ==========
347 |
348 | .. automodule:: the_source
349 | :members:
350 |
351 | """,
352 | )
353 |
354 | with working_dir(srcdir / ".."):
355 | with import_path(["."] + sys.path):
356 | stdout, stderr, output_text = get_sphinx_output(
357 | srcdir,
358 | outdir,
359 | "contents",
360 | )
361 |
362 | expected = "src/contents.rst:3: (vaule) Pass a vaule\n"
363 | assert expected in output_text
364 |
365 |
366 | def test_get_suggestions_to_show_all(sphinx_project):
367 | srcdir, outdir = sphinx_project
368 | add_file(
369 | srcdir,
370 | "conf.py",
371 | """
372 | extensions = ['sphinxcontrib.spelling']
373 | spelling_show_suggestions = True
374 | spelling_suggestion_limit = 0
375 | """,
376 | )
377 | stdout, stderr, app = get_sphinx_app(srcdir, outdir, "contents")
378 | results = app.builder.get_suggestions_to_show(["a", "b", "c"])
379 | assert len(results) == 3
380 |
381 |
382 | def test_get_suggestions_to_show_limit(sphinx_project):
383 | srcdir, outdir = sphinx_project
384 | add_file(
385 | srcdir,
386 | "conf.py",
387 | """
388 | extensions = ['sphinxcontrib.spelling']
389 | spelling_show_suggestions = True
390 | spelling_suggestion_limit = 1
391 | """,
392 | )
393 | stdout, stderr, app = get_sphinx_app(srcdir, outdir, "contents")
394 | results = app.builder.get_suggestions_to_show(["a", "b", "c"])
395 | assert len(results) == 1
396 |
397 |
398 | def test_get_suggestions_to_show_disabled(sphinx_project):
399 | srcdir, outdir = sphinx_project
400 | add_file(
401 | srcdir,
402 | "conf.py",
403 | """
404 | extensions = ['sphinxcontrib.spelling']
405 | spelling_show_suggestions = False
406 | spelling_suggestion_limit = 0
407 | """,
408 | )
409 | stdout, stderr, app = get_sphinx_app(srcdir, outdir, "contents")
410 | results = app.builder.get_suggestions_to_show(["a", "b", "c"])
411 | assert len(results) == 0
412 |
413 |
414 | def test_captions(sphinx_project):
415 | srcdir, outdir = sphinx_project
416 |
417 | add_file(
418 | srcdir,
419 | "contents.rst",
420 | """
421 | The Module
422 | ==========
423 |
424 | .. figure:: blah.gif
425 |
426 | Teh caption
427 |
428 | """,
429 | )
430 |
431 | stdout, stderr, output_text = get_sphinx_output(
432 | srcdir,
433 | outdir,
434 | "contents",
435 | )
436 | assert "(Teh)" in output_text
437 |
438 |
439 | def test_legacy_directive(sphinx_project):
440 | srcdir, outdir = sphinx_project
441 |
442 | add_file(
443 | srcdir,
444 | "contents.rst",
445 | """
446 | The Module
447 | ==========
448 |
449 | .. spelling::
450 |
451 | teh
452 |
453 | teh is OK
454 |
455 | """,
456 | )
457 |
458 | stdout, stderr, output_text = get_sphinx_output(
459 | srcdir,
460 | outdir,
461 | "contents",
462 | )
463 | assert output_text is None
464 |
465 |
466 | def test_domain_directive(sphinx_project):
467 | srcdir, outdir = sphinx_project
468 |
469 | add_file(
470 | srcdir,
471 | "contents.rst",
472 | """
473 | The Module
474 | ==========
475 |
476 | .. spelling:word-list::
477 |
478 | teh
479 |
480 | teh is OK
481 |
482 | """,
483 | )
484 |
485 | stdout, stderr, output_text = get_sphinx_output(
486 | srcdir,
487 | outdir,
488 | "contents",
489 | )
490 | assert output_text is None
491 |
492 |
493 | def test_domain_role(sphinx_project):
494 | srcdir, outdir = sphinx_project
495 |
496 | add_file(
497 | srcdir,
498 | "contents.rst",
499 | """
500 | The Module
501 | ==========
502 |
503 | :spelling:word:`teh` is OK
504 |
505 | """,
506 | )
507 |
508 | stdout, stderr, output_text = get_sphinx_output(
509 | srcdir,
510 | outdir,
511 | "contents",
512 | )
513 | assert output_text is None
514 |
515 |
516 | def test_domain_role_multiple_words(sphinx_project):
517 | srcdir, outdir = sphinx_project
518 |
519 | add_file(
520 | srcdir,
521 | "contents.rst",
522 | """
523 | The Module
524 | ==========
525 |
526 | :spelling:word:`teh is KO`
527 |
528 | """,
529 | )
530 |
531 | stdout, stderr, output_text = get_sphinx_output(
532 | srcdir,
533 | outdir,
534 | "contents",
535 | )
536 | assert output_text is None
537 |
538 |
539 | def test_domain_role_output(sphinx_project):
540 | srcdir, outdir = sphinx_project
541 |
542 | add_file(
543 | srcdir,
544 | "contents.rst",
545 | """
546 | The Module
547 | ==========
548 |
549 | :spelling:word:`teh` is OK
550 |
551 | """,
552 | )
553 |
554 | stdout, stderr, output_text = get_sphinx_output(
555 | srcdir,
556 | outdir,
557 | "contents",
558 | "text",
559 | )
560 |
561 | path = os.path.join(outdir, "contents.txt")
562 | try:
563 | with open(path, "r") as f:
564 | output_text = f.read()
565 | except FileNotFoundError:
566 | output_text = None
567 |
568 | assert output_text == "The Module\n**********\n\nteh is OK\n"
569 |
570 |
571 | def test_domain_ignore(sphinx_project):
572 | srcdir, outdir = sphinx_project
573 |
574 | add_file(
575 | srcdir,
576 | "contents.rst",
577 | """
578 | The Module
579 | ==========
580 |
581 | :spelling:ignore:`baddddd` is OK
582 |
583 | """,
584 | )
585 |
586 | stdout, stderr, output_text = get_sphinx_output(
587 | srcdir,
588 | outdir,
589 | "contents",
590 | )
591 | assert output_text is None
592 |
593 |
594 | def test_domain_ignore_multiple_words(sphinx_project):
595 | srcdir, outdir = sphinx_project
596 |
597 | add_file(
598 | srcdir,
599 | "contents.rst",
600 | """
601 | The Module
602 | ==========
603 |
604 | :spelling:ignore:`baddddd` is OK here.
605 |
606 | But, baddddd is not OK here.
607 | Nor, here baddddd.
608 |
609 | """,
610 | )
611 |
612 | stdout, stderr, output_text = get_sphinx_output(
613 | srcdir,
614 | outdir,
615 | "contents",
616 | )
617 | assert "(baddddd)" in output_text
618 | assert output_text.count("\n") == 2 # Only expect 2 errors, not 3.
619 |
620 |
621 | def test_domain_ignore_output(sphinx_project):
622 | srcdir, outdir = sphinx_project
623 |
624 | add_file(
625 | srcdir,
626 | "contents.rst",
627 | """
628 | The Module
629 | ==========
630 |
631 | :spelling:ignore:`teh` is OK
632 |
633 | """,
634 | )
635 |
636 | stdout, stderr, output_text = get_sphinx_output(
637 | srcdir,
638 | outdir,
639 | "contents",
640 | "text",
641 | )
642 |
643 | path = os.path.join(outdir, "contents.txt")
644 | try:
645 | with open(path, "r") as f:
646 | output_text = f.read()
647 | except FileNotFoundError:
648 | output_text = None
649 |
650 | assert output_text == "The Module\n**********\n\nteh is OK\n"
651 |
652 |
653 | def test_only_directive(sphinx_project):
654 | # How to skip checking nested blocks of content
655 | # https://github.com/sphinx-contrib/spelling/issues/204
656 | srcdir, outdir = sphinx_project
657 |
658 | add_file(
659 | srcdir,
660 | "contents.rst",
661 | """
662 | The Module
663 | ==========
664 |
665 | .. only:: html
666 |
667 | teh is ok
668 |
669 | whaat is not ok
670 | """,
671 | )
672 |
673 | stdout, stderr, output_text = get_sphinx_output(
674 | srcdir,
675 | outdir,
676 | "contents",
677 | )
678 | assert "(whaat)" in output_text
679 | assert "(teh)" not in output_text
680 |
--------------------------------------------------------------------------------
/tests/test_checker.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Tests for SpellingChecker."""
5 |
6 | import os
7 |
8 | from sphinxcontrib.spelling.checker import SpellingChecker, line_of_index
9 |
10 |
11 | def test_errors_only():
12 | checker = SpellingChecker(
13 | lang="en_US",
14 | suggest=False,
15 | word_list_filename=None,
16 | )
17 | for word, suggestions, line, offset in checker.check("This txt is wrong"):
18 | assert not suggestions, "Suggesting"
19 | assert word == "txt"
20 | assert line == ""
21 | assert offset == 0
22 |
23 |
24 | def test_with_suggestions():
25 | checker = SpellingChecker(
26 | lang="en_US",
27 | suggest=True,
28 | word_list_filename=None,
29 | )
30 | for word, suggestions, line, offset in checker.check("This txt is wrong"):
31 | assert suggestions, "Not suggesting"
32 | assert word == "txt"
33 | assert line == ""
34 | assert offset == 0
35 |
36 |
37 | def test_with_wordlist():
38 | checker = SpellingChecker(
39 | lang="en_US",
40 | suggest=False,
41 | word_list_filename=os.path.join(os.path.dirname(__file__), "test_wordlist.txt"),
42 | )
43 | words = [
44 | word for word, suggestions, line, offset in checker.check("This txt is wrong")
45 | ]
46 | assert not words, "Did not use personal word list file"
47 |
48 |
49 | def test_with_context_line():
50 | checker = SpellingChecker(
51 | lang="en_US",
52 | suggest=False,
53 | word_list_filename=None,
54 | context_line=True,
55 | )
56 |
57 | text = "Line one\nThis txt is wrong\nLine two"
58 | for word, suggestions, line, offset in checker.check(text):
59 | assert not suggestions, "Suggesting"
60 | assert word == "txt"
61 | assert line == "This txt is wrong"
62 | assert offset == 1
63 |
64 |
65 | def test_line_of_index_one_line():
66 | text = "foo bar baz"
67 | assert line_of_index(text, 0) == text
68 | assert line_of_index(text, 5) == text
69 | assert line_of_index(text, len(text)) == text
70 |
71 |
72 | def test_line_of_index_multi_line():
73 | text = "\nfoo\n\nbar baz\n"
74 |
75 | assert line_of_index(text, 0) == ""
76 |
77 | assert line_of_index(text, 1) == "foo"
78 | assert line_of_index(text, 2) == "foo"
79 | assert line_of_index(text, 3) == "foo"
80 | assert line_of_index(text, 4) == "foo"
81 |
82 | assert line_of_index(text, 5) == ""
83 |
84 | assert line_of_index(text, 6) == "bar baz"
85 | assert line_of_index(text, 12) == "bar baz"
86 | assert line_of_index(text, 13) == "bar baz"
87 |
88 | assert line_of_index(text, 14) == ""
89 |
--------------------------------------------------------------------------------
/tests/test_filter.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2010 Doug Hellmann. All rights reserved.
3 | #
4 | """Tests for filters."""
5 |
6 | import contextlib
7 | import logging
8 | import os
9 | import sys
10 |
11 | import pytest
12 | from enchant.tokenize import get_tokenizer
13 |
14 | from sphinxcontrib.spelling import filters # isort:skip
15 | from tests import helpers # isort:skip
16 |
17 | # Replace the sphinx logger with a normal one so pytest can collect
18 | # the output.
19 | filters.logger = logging.getLogger("test.filters")
20 |
21 |
22 | def test_builtin_unicode():
23 | f = filters.PythonBuiltinsFilter(None)
24 | assert not f._skip("passé")
25 |
26 |
27 | def test_builtin_regular():
28 | f = filters.PythonBuiltinsFilter(None)
29 | assert f._skip("print")
30 |
31 |
32 | def test_acronym():
33 | text = "a front-end for DBM-style databases"
34 | t = get_tokenizer("en_US", [])
35 | f = filters.AcronymFilter(t)
36 | words = [w[0] for w in f(text)]
37 | assert "DBM" not in words, "Failed to filter out acronym"
38 |
39 |
40 | def test_acronym_unicode():
41 | text = "a front-end for DBM-style databases"
42 | t = get_tokenizer("en_US", [])
43 | f = filters.AcronymFilter(t)
44 | words = [w[0] for w in f(text)]
45 | assert "DBM" not in words, "Failed to filter out acronym"
46 |
47 |
48 | @helpers.require_git_repo
49 | @pytest.mark.parametrize(
50 | "name",
51 | [
52 | "Alex",
53 | "Atlakson",
54 | "Avram",
55 | "Baumgold",
56 | "Berman",
57 | "Daniele",
58 | "Doug",
59 | "Finucane",
60 | "Gaynor",
61 | "Gonsiorowski",
62 | "Hong",
63 | "Hong",
64 | "Huon",
65 | "Kampik",
66 | "Kolosov",
67 | "Lubkin",
68 | "Marti",
69 | "Minhee",
70 | "Olausson",
71 | "Raggam",
72 | "Raudsepp",
73 | "sdelliot",
74 | "Sergey",
75 | "Sevilla",
76 | "Timotheus",
77 | "Tobias",
78 | "Tricoli",
79 | ],
80 | )
81 | def test_contributors(name):
82 | f = filters.ContributorFilter(None)
83 | assert f._skip(name)
84 |
85 |
86 | @pytest.mark.parametrize(
87 | "word,expected",
88 | [
89 | ("os", True),
90 | ("os.name", False),
91 | ("__main__", False),
92 | ("don't", False),
93 | ],
94 | )
95 | def test_importable_module_skip(word, expected):
96 | f = filters.ImportableModuleFilter(None)
97 | assert f._skip(word) is expected
98 |
99 |
100 | @contextlib.contextmanager
101 | def import_path(new_path):
102 | "Temporarily change sys.path for imports."
103 | before = sys.path
104 | try:
105 | sys.path = new_path
106 | yield
107 | finally:
108 | sys.path = before
109 |
110 |
111 | def test_importable_module_with_side_effets(tmpdir):
112 | logging.debug("tmpdir %r", tmpdir)
113 | logging.debug("cwd %r", os.getcwd())
114 |
115 | parentdir = tmpdir.join("parent")
116 | parentdir.mkdir()
117 |
118 | parentdir.join("__init__.py").write('raise SystemExit("exit as side-effect")\n')
119 | parentdir.join("child.py").write("")
120 |
121 | with import_path([str(tmpdir)] + sys.path):
122 | f = filters.ImportableModuleFilter(None)
123 | skip_parent = f._skip("parent")
124 | skip_both = f._skip("parent.child")
125 |
126 | # The parent module name is valid because it is not imported, only
127 | # discovered.
128 | assert skip_parent is True
129 | assert "parent" in f.found_modules
130 |
131 | # The child module name is not valid because the parent is
132 | # imported to find the child and that triggers the side-effect.
133 | assert skip_both is False
134 | assert "parent.child" not in f.found_modules
135 |
136 |
137 | def test_importable_module_with_system_exit(tmpdir):
138 | path = tmpdir.join("mytestmodule.py")
139 | path.write('raise SystemExit("exit as side-effect")\n')
140 |
141 | with import_path([str(tmpdir)] + sys.path):
142 | f = filters.ImportableModuleFilter(None)
143 | skip = f._skip("mytestmodule")
144 |
145 | # The filter does not actually import the module in this case, so
146 | # it shows up as a valid word.
147 | assert skip is True
148 | assert "mytestmodule" in f.found_modules
149 |
150 |
151 | def test_pypi_filter_factory():
152 | f = filters.PyPIFilterFactory()
153 | assert "sphinxcontrib-spelling" in f.words
154 | assert "setuptools" in f.words
155 |
--------------------------------------------------------------------------------
/tests/test_wordlist.txt:
--------------------------------------------------------------------------------
1 | txt
2 |
--------------------------------------------------------------------------------
/tools/history-update.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -x
4 |
5 | git remote -v
6 |
7 | git branch -a
8 |
9 | # We only look at the files that have changed in the current PR, to
10 | # avoid problems when the template is changed in a way that is
11 | # incompatible with existing documents.
12 | if git log --name-only --pretty= "origin/main.." -- \
13 | | grep -q '^docs/source/history.rst$'; then
14 | echo "Found a change to history file."
15 | exit 0
16 | fi
17 |
18 | echo "PRs must include a change in docs/source/history.rst"
19 | exit 1
20 |
--------------------------------------------------------------------------------