├── MANIFEST.in
├── docs
├── _vale
│ ├── ignore_words.txt
│ ├── custom
│ │ └── Spelling.yml
│ └── write-good
│ │ ├── meta.json
│ │ ├── So.yml
│ │ ├── ThereIs.yml
│ │ ├── Illusions.yml
│ │ ├── E-Prime.yml
│ │ ├── README.md
│ │ ├── Passive.yml
│ │ ├── Weasel.yml
│ │ ├── TooWordy.yml
│ │ └── Cliches.yml
├── source
│ ├── changelog.rst
│ ├── getting-started.rst
│ ├── _static
│ │ └── sitemap-icon.svg
│ ├── search-optimization.rst
│ ├── contributing.rst
│ ├── configuration-values.rst
│ ├── index.rst
│ ├── conf.py
│ └── advanced-configuration.rst
├── requirements.txt
└── Makefile
├── tests
├── roots
│ └── test-root
│ │ ├── conf.py
│ │ ├── bar.rst
│ │ ├── foo.rst
│ │ ├── dolor.rst
│ │ ├── elitr.rst
│ │ ├── ipsum.rst
│ │ ├── lorem.rst
│ │ └── index.rst
├── conftest.py
├── test_parallel_mode.py
└── test_simple.py
├── requirements_dev.txt
├── readthedocs.yml
├── .vale.ini
├── .gitignore
├── tox.ini
├── .pre-commit-config.yaml
├── MAINTENANCE.md
├── .github
├── dependabot.yml
├── FUNDING.yml
└── workflows
│ └── continuous-integration.yml
├── LICENSE
├── pyproject.toml
├── README.rst
├── CHANGELOG.rst
└── sphinx_sitemap
└── __init__.py
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 |
--------------------------------------------------------------------------------
/docs/_vale/ignore_words.txt:
--------------------------------------------------------------------------------
1 | Conda
2 | Algolia
3 |
--------------------------------------------------------------------------------
/docs/source/changelog.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../../CHANGELOG.rst
2 |
--------------------------------------------------------------------------------
/tests/roots/test-root/conf.py:
--------------------------------------------------------------------------------
1 | extensions = ["sphinx_sitemap"]
2 |
--------------------------------------------------------------------------------
/tests/roots/test-root/bar.rst:
--------------------------------------------------------------------------------
1 | bar
2 | ===
3 |
4 | This is a bar page
5 |
--------------------------------------------------------------------------------
/tests/roots/test-root/foo.rst:
--------------------------------------------------------------------------------
1 | foo
2 | ===
3 |
4 | This is a foo page
5 |
--------------------------------------------------------------------------------
/tests/roots/test-root/dolor.rst:
--------------------------------------------------------------------------------
1 | :orphan:
2 |
3 | Dolor
4 | =====
5 |
6 | This is the dolor page
7 |
--------------------------------------------------------------------------------
/tests/roots/test-root/elitr.rst:
--------------------------------------------------------------------------------
1 | :orphan:
2 |
3 | Elitr
4 | =====
5 |
6 | This is the elitr page
7 |
--------------------------------------------------------------------------------
/tests/roots/test-root/ipsum.rst:
--------------------------------------------------------------------------------
1 | :orphan:
2 |
3 | Ipsum
4 | =====
5 |
6 | This is the ipsum page
7 |
--------------------------------------------------------------------------------
/tests/roots/test-root/lorem.rst:
--------------------------------------------------------------------------------
1 | :orphan:
2 |
3 | Lorem
4 | =====
5 |
6 | This is the lorem page
7 |
--------------------------------------------------------------------------------
/requirements_dev.txt:
--------------------------------------------------------------------------------
1 | tox
2 | build
3 | pre-commit
4 | flake8
5 | sphinx
6 | sphinx-last-updated-by-git
7 | pytest
8 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | furo
2 | esbonio
3 | sphinx-contributors
4 | sphinx
5 | sphinx-sitemap
6 | sphinxemoji
7 | sphinxext-opengraph
8 |
--------------------------------------------------------------------------------
/tests/roots/test-root/index.rst:
--------------------------------------------------------------------------------
1 | test for basic sitemap
2 | ======================
3 |
4 | .. toctree::
5 |
6 | foo
7 | bar
8 |
--------------------------------------------------------------------------------
/docs/_vale/custom/Spelling.yml:
--------------------------------------------------------------------------------
1 | extends: spelling
2 | message: "Did you really mean '%s'?"
3 | level: error
4 | ignore:
5 | - ignore_words.txt
6 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "feed": "https://github.com/errata-ai/write-good/releases.atom",
3 | "vale_version": ">=1.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/So.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "Don't start a sentence with '%s'."
3 | level: error
4 | raw:
5 | - '(?:[;-]\s)so[\s,]|\bSo[\s,]'
6 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/ThereIs.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "Don't start a sentence with '%s'."
3 | ignorecase: false
4 | level: error
5 | raw:
6 | - '(?:[;-]\s)There\s(is|are)|\bThere\s(is|are)\b'
7 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/Illusions.yml:
--------------------------------------------------------------------------------
1 | extends: repetition
2 | message: "'%s' is repeated!"
3 | level: warning
4 | alpha: true
5 | action:
6 | name: edit
7 | params:
8 | - truncate
9 | - " "
10 | tokens:
11 | - '[^\s]+'
12 |
--------------------------------------------------------------------------------
/readthedocs.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | build:
4 | os: "ubuntu-20.04"
5 | tools:
6 | python: "3.10"
7 |
8 | sphinx:
9 | configuration: docs/source/conf.py
10 |
11 | python:
12 | install:
13 | - requirements: docs/requirements.txt
14 | - method: pip
15 | path: .
16 |
--------------------------------------------------------------------------------
/.vale.ini:
--------------------------------------------------------------------------------
1 | StylesPath = ./docs/_vale
2 | MinAlertLevel = suggestion
3 |
4 | # SphinxBuildPath = docs/_build
5 | # SphinxAutoBuild = make html
6 |
7 | Packages = write-good, Microsoft
8 |
9 | [*.rst]
10 | BasedOnStyles = Vale, custom
11 |
12 | Vale.Redundancy = YES
13 | Vale.Repetition = YES
14 | Vale.GenderBias = YES
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .idea/
3 | *.code-workspace
4 |
5 | # Unit test / coverage reports
6 | .tox
7 |
8 | # Distribution / packaging
9 | *.egg-info/
10 | dist/
11 | build/
12 |
13 | # Environments
14 | .venv
15 |
16 | # Sphinx documentation
17 | docs/_build/
18 |
19 | # vale packages
20 | docs/_vale/Microsoft/
21 | docs/_vale/write-good/
22 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/E-Prime.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "Try to avoid using '%s'."
3 | ignorecase: true
4 | level: suggestion
5 | tokens:
6 | - am
7 | - are
8 | - aren't
9 | - be
10 | - been
11 | - being
12 | - he's
13 | - here's
14 | - here's
15 | - how's
16 | - i'm
17 | - is
18 | - isn't
19 | - it's
20 | - she's
21 | - that's
22 | - there's
23 | - they're
24 | - was
25 | - wasn't
26 | - we're
27 | - were
28 | - weren't
29 | - what's
30 | - where's
31 | - who's
32 | - you're
33 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist =
3 | py39-sphinx{5,6,7,last}
4 | # Python 3.10 is unsupported below Sphinx4
5 | # See https://github.com/sphinx-doc/sphinx/issues/9816
6 | py3{10,11,12}-sphinx{5,6,7,last}
7 |
8 | [testenv]
9 | deps =
10 | gitpython
11 | pytest
12 | sphinx5: Sphinx[test]~=5.0
13 | sphinx6: Sphinx[test]~=6.0
14 | sphinx7: Sphinx[test]~=7.0
15 | sphinxlast: Sphinx[test]
16 | commands =
17 | pytest -W ignore::DeprecationWarning
18 |
19 | [flake8]
20 | max-line-length = 100
21 | extend-ignore = E203
22 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # Install pre-commit hooks via
2 | # pre-commit install
3 |
4 | repos:
5 |
6 | - repo: https://github.com/PyCQA/isort
7 | rev: 7.0.0
8 | hooks:
9 | - id: isort
10 |
11 | - repo: https://github.com/psf/black-pre-commit-mirror
12 | rev: 25.11.0
13 | hooks:
14 | - id: black
15 |
16 | - repo: https://github.com/PyCQA/flake8
17 | rev: 7.3.0
18 | hooks:
19 | - id: flake8
20 |
21 | - repo: https://github.com/sphinx-contrib/sphinx-lint
22 | rev: v1.0.2
23 | hooks:
24 | - id: sphinx-lint
25 | args: [--jobs=1]
26 | files: ^docs/|CHANGELOG.rst|README.rst
27 | types: [rst]
28 |
--------------------------------------------------------------------------------
/MAINTENANCE.md:
--------------------------------------------------------------------------------
1 | # Maintaining PyPI Version
2 |
3 | These are the steps, to be run by the maintainer, for making a new Python
4 | package release.
5 |
6 | 1. Rev `__version__` in **sphinx_sitemap/\_\_init\_\_.py**.
7 | 2. Update **CHANGELOG.rst**
8 | 3. Create a tag and push to GitHub:
9 |
10 | git tag -a vX.Y.Z -m "Release vX.Y.Z"
11 | git push --tags origin master
12 |
13 | 4. Build the latest distribution locally:
14 |
15 | python -m build
16 |
17 | 5. Upload to the test pypi.org repository:
18 |
19 | twine upload --repository testpypi dist/*
20 |
21 | 6. Upload to the production pypi.org repository:
22 |
23 | twine upload dist/*
24 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SPHINXPROJ = SphinxSitemap
8 | SOURCEDIR = source
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "github-actions"
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "monthly"
12 | groups:
13 | # Name for the group, which will be used in PR titles and branch names
14 | all-github-actions:
15 | # Group all updates together
16 | patterns:
17 | - "*"
18 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 | import sphinx
5 |
6 | pytest_plugins = "sphinx.testing.fixtures"
7 | # Exclude 'roots' dirs for pytest test collector
8 | collect_ignore = ["roots"]
9 |
10 |
11 | def pytest_configure(config):
12 | # before Sphinx 3.3.0, the `sphinx` marker is not registered by
13 | # the extension (but by Sphinx's internal pytest config)
14 | config.addinivalue_line("markers", "sphinx")
15 |
16 |
17 | @pytest.fixture(scope="session")
18 | def rootdir():
19 | if sphinx.version_info[:2] < (7, 2):
20 | from sphinx.testing.path import path
21 |
22 | return path(__file__).parent.abspath() / "roots"
23 |
24 | return Path(__file__).resolve().parent / "roots"
25 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [jdillard]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Jared Dillard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/source/getting-started.rst:
--------------------------------------------------------------------------------
1 | Getting Started
2 | ===============
3 |
4 | Installation
5 | ------------
6 |
7 | Directly install via ``pip`` by using:
8 |
9 | .. code::
10 |
11 | pip install sphinx-sitemap
12 |
13 | Or with ``conda`` via ``conda-forge``:
14 |
15 | .. code::
16 |
17 | conda install -c conda-forge sphinx-sitemap
18 |
19 | Usage
20 | -----
21 |
22 | Add ``sphinx_sitemap`` to :confval:`extensions` in your Sphinx **conf.py**.
23 | For example:
24 |
25 | .. code:: python
26 |
27 | extensions = ['sphinx_sitemap']
28 |
29 | Set the value of :confval:`html_baseurl` in your Sphinx **conf.py** to the current
30 | base URL of your documentation. For example:
31 |
32 | .. code:: python
33 |
34 | html_baseurl = 'https://my-site.com/docs/'
35 |
36 | After the HTML finishes building, **sphinx-sitemap** will output the location of the sitemap::
37 |
38 | sitemap.xml was generated for URL https://my-site.com/docs/ in /path/to/_build/sitemap.xml
39 |
40 | .. tip:: Make sure to confirm the accuracy of the sitemap after installs and upgrades.
41 |
42 | See :doc:`advanced-configuration` for more information about how to use **sphinx-sitemap**.
43 |
--------------------------------------------------------------------------------
/docs/source/_static/sitemap-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/tests/test_parallel_mode.py:
--------------------------------------------------------------------------------
1 | import os
2 | from xml.etree import ElementTree as etree
3 |
4 | import pytest
5 | from git import Repo
6 |
7 |
8 | @pytest.fixture(autouse=True, scope="function")
9 | def git_setup(app):
10 | repo = Repo.init(app.srcdir)
11 | repo.index.add(os.listdir(app.srcdir))
12 | repo.index.commit("test: creating git record for files")
13 | yield
14 |
15 |
16 | @pytest.mark.sphinx(
17 | "html",
18 | freshenv=True,
19 | confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"},
20 | )
21 | def test_parallel(app, status, warning):
22 | app.parallel = 2
23 | app.warningiserror = True
24 | app.build()
25 | assert "sitemap.xml" in os.listdir(app.outdir)
26 | doc = etree.parse(app.outdir / "sitemap.xml")
27 | urls = {
28 | e.text
29 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
30 | }
31 |
32 | assert urls == {
33 | f"https://example.org/docs/en/{d}.html"
34 | for d in [
35 | "index",
36 | "foo",
37 | "bar",
38 | "lorem",
39 | "ipsum",
40 | "dolor",
41 | "elitr",
42 | "genindex",
43 | "search",
44 | ]
45 | }
46 | assert not warning.getvalue()
47 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/README.md:
--------------------------------------------------------------------------------
1 | Based on [write-good](https://github.com/btford/write-good).
2 |
3 | > Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too.
4 |
5 | ```
6 | The MIT License (MIT)
7 |
8 | Copyright (c) 2014 Brian Ford
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 | ```
28 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools",
4 | ]
5 | build-backend = "setuptools.build_meta"
6 |
7 | [project]
8 | name = "sphinx-sitemap"
9 | description = "Sitemap generator for Sphinx"
10 | authors = [
11 | {name = "Jared Dillard", email = "jared.dillard@gmail.com"},
12 | ]
13 | maintainers = [
14 | {name = "Jared Dillard", email = "jared.dillard@gmail.com"},
15 | ]
16 | classifiers = [
17 | "Framework :: Sphinx :: Extension",
18 | "Programming Language :: Python :: 3.9",
19 | "Programming Language :: Python :: 3.10",
20 | "Programming Language :: Python :: 3.11",
21 | "Topic :: Documentation :: Sphinx",
22 | ]
23 | license = "MIT"
24 | license-files = ["LICENSE"]
25 | readme = "README.rst"
26 | dependencies = [
27 | "sphinx-last-updated-by-git",
28 | ]
29 | dynamic = [
30 | "optional-dependencies",
31 | "version",
32 | ]
33 |
34 | [project.urls]
35 | documentation = "https://sphinx-sitemap.readthedocs.io/en/latest/index.html"
36 | download = "https://pypi.org/project/sphinx-sitemap/"
37 | source = "https://github.com/jdillard/sphinx-sitemap"
38 | changelog = "https://github.com/jdillard/sphinx-sitemap/blob/master/CHANGELOG.rst"
39 |
40 | [tool.setuptools.dynamic]
41 | optional-dependencies = {dev = { file = ["requirements_dev.txt"] }}
42 | version = {attr = "sphinx_sitemap.__version__"}
43 |
44 | [tool.isort]
45 | profile = "black"
46 |
--------------------------------------------------------------------------------
/docs/source/search-optimization.rst:
--------------------------------------------------------------------------------
1 | Getting the Most out of the Sitemap
2 | ===================================
3 |
4 | Search Engine Optimization
5 | --------------------------
6 |
7 | Using robots.txt
8 | ^^^^^^^^^^^^^^^^
9 |
10 | Add a **robots.txt** file in the **source** directory which has a link to the ``sitemap.xml`` or ``sitemapindex.xml`` file. For example::
11 |
12 | User-agent: *
13 |
14 | Sitemap: https://my-site.com/docs/sitemap.xml
15 |
16 | Then, add **robots.txt** to :confval:`html_extra_path` in **conf.py**:
17 |
18 | .. code-block:: python
19 |
20 | html_extra_path = ['robots.txt']
21 |
22 | Submit Sitemap to Search Engines
23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24 |
25 | Submit the ``sitemap.xml`` or ``sitemapindex.xml`` to the appropriate search engine tools.
26 |
27 | Site Search Optimization
28 | ------------------------
29 |
30 | Site search crawlers can also take advantage of sitemaps as starting points for crawling.
31 |
32 | Examples:
33 |
34 | - `Algolia`_
35 |
36 | .. _Algolia: https://www.algolia.com/doc/tools/crawler/apis/configuration/sitemaps/
37 |
38 | .. _rag-ingestion:
39 |
40 | RAG (Retrieval-Augmented Generation) Ingestion
41 | -----------------------------------------------
42 |
43 | The sitemap can be used as a structured data source for RAG systems to efficiently discover and ingest documentation content.
44 |
45 | - **Comprehensive Discovery**: The sitemap provides a complete list of all documentation pages, ensuring no content is missed during ingestion
46 | - **Incremental Updates**: Use the ```` timestamps to identify recently updated content and refresh only those embeddings in your RAG system.
47 |
--------------------------------------------------------------------------------
/docs/source/contributing.rst:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | You will need to set up a development environment to make and test your changes
5 | before submitting them.
6 |
7 | Local development
8 | -----------------
9 |
10 | #. Clone the `sphinx-sitemap repository`_.
11 |
12 | #. Create and activate a virtual environment:
13 |
14 | .. code-block:: console
15 |
16 | python3 -m venv .venv
17 | source .venv/bin/activate
18 |
19 | #. Install development dependencies:
20 |
21 | .. code-block:: console
22 |
23 | pip3 install -r requirements_dev.txt
24 |
25 | #. Install pre-commit Git hook scripts:
26 |
27 | .. code-block:: console
28 |
29 | pre-commit install
30 |
31 | Install a local copy of the extension
32 | -------------------------------------
33 |
34 | Add **sphinx-sitemap** as a `third party extension`_.
35 |
36 | #. If your project doesn't have an extensions directory, create ``_exts`` and
37 | point **conf.py** to it:
38 |
39 | .. code-block:: python
40 |
41 | sys.path.append(os.path.abspath('../_exts'))
42 |
43 | #. Copy ``sphinx_sitemap`` as a directory in your project's extensions
44 | directory, and rename it to ``sphinx_sitemap_dev``.
45 |
46 | #. Add ``sphinx_sitemap_dev`` to :confval:`extensions`, or if already installed via ``pip``, change ``sphinx_sitemap`` to ``sphinx_sitemap_dev`` in **conf.py**:
47 |
48 | .. code-block:: python
49 |
50 | extensions = ['sphinx_sitemap_dev']
51 |
52 | You can now make changes to ``sphinx_sitemap_dev``.
53 |
54 | Testing changes
55 | ---------------
56 |
57 | Run ``tox`` before committing changes.
58 |
59 | Current contributors
60 | --------------------
61 |
62 | Thanks to all who have contributed!
63 | The people that have improved the code:
64 |
65 | .. contributors:: jdillard/sphinx-sitemap
66 | :avatars:
67 | :limit: 100
68 | :exclude: pre-commit-ci[bot],dependabot[bot]
69 | :order: ASC
70 |
71 |
72 | .. _sphinx-sitemap repository: https://github.com/jdillard/sphinx-sitemap
73 | .. _third party extension: http://www.sphinx-doc.org/en/master/ext/thirdparty.html
74 |
--------------------------------------------------------------------------------
/docs/source/configuration-values.rst:
--------------------------------------------------------------------------------
1 | Project Configuration Values
2 | ============================
3 |
4 | A list of of possible configuration values to configure in **conf.py**:
5 |
6 | .. confval:: sitemap_url_scheme
7 |
8 | - **Type**: string
9 | - **Default**: ``'{lang}{version}{link}'``
10 | - **Description**: The scheme used for URL structure.
11 | See :ref:`configuration_customizing_url_scheme` for more information.
12 |
13 | .. versionadded:: 2.0.0
14 |
15 | .. confval:: sitemap_filename
16 |
17 | - **Type**: string
18 | - **Default**: ``'sitemap.xml'``
19 | - **Description**: The filename used for the sitemap.
20 | See :ref:`configuration_changing_filename` for more information.
21 |
22 | .. versionadded:: 2.2.0
23 |
24 | .. confval:: sitemap_locales
25 |
26 | - **Type**: list of strings
27 | - **Default**: ``[]`` (empty list)
28 | - **Description**: The list of locales to include in the sitemap.
29 | See :ref:`configuration_supporting_multiple_languages` for more information.
30 |
31 | .. versionadded:: 2.2.0
32 |
33 | .. confval:: sitemap_excludes
34 |
35 | - **Type**: list of strings
36 | - **Default**: ``[]`` (empty list)
37 | - **Description**: The list of pages to exclude from the sitemap.
38 | Supports wildcard patterns.
39 | See :ref:`configuration_excluding_pages` for more information.
40 |
41 | .. versionadded:: 2.6.0
42 |
43 | .. versionchanged:: 2.8.0
44 | Added support for Unix-style wildcard patterns.
45 |
46 | .. confval:: sitemap_show_lastmod
47 |
48 | - **Type**: boolean
49 | - **Default**: ``False``
50 | - **Description**: Add ```` to sitemap based on last updated time according to Git for each page.
51 | See :ref:`configuration_lastmod` for more information.
52 |
53 | .. versionadded:: 2.7.0
54 |
55 | .. confval:: sitemap_indent
56 |
57 | - **Type**: integer
58 | - **Default**: ``0``
59 | - **Description**: Number of spaces to use for indentation in the sitemap XML output.
60 | See :ref:`configuration_indent` for more information.
61 |
62 | .. versionadded:: 2.9.0
63 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | sphinx-sitemap
2 | ==============
3 |
4 | A `Sphinx`_ extension to generate multi-version and multi-language
5 | `sitemaps.org`_ compliant sitemaps for the HTML version of your Sphinx
6 | documentation.
7 |
8 | |PyPI version| |Conda Forge| |Downloads| |Parallel Safe| |GitHub Stars|
9 |
10 | Demo
11 | ----
12 |
13 | You can see this Sphinx project's `sitemap.xml`_ as a simple example.
14 |
15 | Highlights
16 | ----------
17 |
18 | 1. **Multi-version Support**: Generates sitemaps for multiple documentation versions
19 | 2. **Multi-language Support**: Creates sitemaps for multiple language variants of your documentation
20 | 3. **Content Exclusion**: Allows you to exclude specific pages or patterns from the sitemap
21 | 4. **Last Modified Support**: Includes lastmod timestamps to help understand content freshness
22 | 5. **AI & RAG Optimization**: Helps AI systems discover and index your documentation content
23 | 6. **SEO Optimization**: Improves search engine discoverability of your documentation
24 |
25 | .. toctree::
26 | :maxdepth: 2
27 |
28 | getting-started
29 | advanced-configuration
30 | search-optimization
31 | configuration-values
32 | contributing
33 | changelog
34 |
35 |
36 | .. _sitemaps.org: https://www.sitemaps.org/protocol.html
37 | .. _sitemap.xml: https://sphinx-sitemap.readthedocs.io/en/latest/sitemap.xml
38 | .. _Sphinx: http://sphinx-doc.org/
39 |
40 | .. |PyPI version| image:: https://img.shields.io/pypi/v/sphinx-sitemap.svg
41 | :target: https://pypi.python.org/pypi/sphinx-sitemap
42 | :alt: Latest PyPi Version
43 | .. |Conda Forge| image:: https://img.shields.io/conda/vn/conda-forge/sphinx-sitemap.svg
44 | :target: https://anaconda.org/conda-forge/sphinx-sitemap
45 | :alt: Latest Conda Forge version
46 | .. |Downloads| image:: https://static.pepy.tech/badge/sphinx-sitemap/month
47 | :target: https://pepy.tech/project/sphinx-sitemap
48 | :alt: PyPi Downloads per month
49 | .. |Parallel Safe| image:: https://img.shields.io/badge/parallel%20safe-true-brightgreen
50 | :target: #
51 | :alt: Parallel read/write safe
52 | .. |GitHub Stars| image:: https://img.shields.io/github/stars/jdillard/sphinx-sitemap?style=social
53 | :target: https://github.com/jdillard/sphinx-sitemap
54 | :alt: GitHub Repository stars
55 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Sphinx Sitemap Generator Extension
2 | ==================================
3 |
4 | A `Sphinx`_ extension to generate multiversion and multilanguage
5 | `sitemaps.org`_ compliant sitemaps for the HTML version of your Sphinx
6 | documentation.
7 |
8 | | |PyPI version| |Conda Forge| |Downloads| |Parallel Safe|
9 | | |Code style: Black| |Docs Build| |CI Workflow|
10 |
11 | Documentation
12 | -------------
13 |
14 | See `sphinx-sitemap documentation`_ for installation and configuration instructions.
15 |
16 | Contributing
17 | ------------
18 |
19 | Pull Requests welcome! See `Contributing`_ for instructions on how best to contribute.
20 |
21 | License
22 | -------
23 |
24 | **sphinx-sitemap** is made available under a **MIT license**; see `LICENSE`_ for
25 | details.
26 |
27 | Originally based on the sitemap generator in the `guzzle_sphinx_theme`_ project,
28 | also licensed under the MIT license.
29 |
30 | .. _Contributing: https://sphinx-sitemap.readthedocs.io/en/latest/contributing.html
31 | .. _guzzle_sphinx_theme: https://github.com/guzzle/guzzle_sphinx_theme
32 | .. _LICENSE: LICENSE
33 | .. _Sphinx: http://sphinx-doc.org/
34 | .. _sitemaps.org: https://www.sitemaps.org/protocol.html
35 | .. _sphinx-sitemap documentation: https://sphinx-sitemap.readthedocs.io/en/latest/index.html
36 |
37 | .. |PyPI version| image:: https://img.shields.io/pypi/v/sphinx-sitemap.svg
38 | :target: https://pypi.python.org/pypi/sphinx-sitemap
39 | .. |Conda Forge| image:: https://img.shields.io/conda/vn/conda-forge/sphinx-sitemap.svg
40 | :target: https://anaconda.org/conda-forge/sphinx-sitemap
41 | .. |Downloads| image:: https://static.pepy.tech/badge/sphinx-sitemap/month
42 | :target: https://pepy.tech/project/sphinx-sitemap
43 | .. |Code style: Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
44 | :target: https://github.com/psf/black
45 | .. |Parallel Safe| image:: https://img.shields.io/badge/parallel%20safe-true-brightgreen
46 | :target: #
47 | .. |Docs Build| image:: https://readthedocs.org/projects/sphinx-sitemap/badge/?version=latest
48 | :target: https://sphinx-sitemap.readthedocs.io/en/latest/?badge=latest
49 | :alt: Documentation Status
50 | .. |CI Workflow| image:: https://github.com/jdillard/sphinx-sitemap/actions/workflows/continuous-integration.yml/badge.svg
51 | :target: https://github.com/jdillard/sphinx-sitemap/actions/workflows/continuous-integration.yml
52 |
--------------------------------------------------------------------------------
/.github/workflows/continuous-integration.yml:
--------------------------------------------------------------------------------
1 |
2 | name: continuous-integration
3 | on:
4 | push:
5 | branches: [master]
6 | tags:
7 | - "v[0-9]+.[0-9]+.[0-9]+*"
8 | pull_request:
9 | jobs:
10 | pre-commit:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v6
14 | - name: Set up Python 3.10
15 | uses: actions/setup-python@v6
16 | with:
17 | python-version: "3.10"
18 | - uses: pre-commit/action@v3.0.1
19 | tests:
20 | runs-on: ubuntu-latest
21 | strategy:
22 | matrix:
23 | python-version: ['3.9', '3.10', '3.11', '3.12']
24 | sphinx-version: ['']
25 | include:
26 | - python-version: '3.12'
27 | sphinx-version: 'dev'
28 | - python-version: '3.11'
29 | sphinx-version: '7'
30 | - python-version: '3.10'
31 | sphinx-version: '6'
32 | - python-version: '3.9'
33 | sphinx-version: '5'
34 | steps:
35 | - uses: actions/checkout@v6
36 | - name: Setup Python versions
37 | uses: actions/setup-python@v6
38 | with:
39 | python-version: ${{ matrix.python-version }}
40 | - name: Install Python dependencies
41 | run: |
42 | set -eo pipefail
43 | if [[ "${{ matrix.sphinx-version }}" != "" ]]; then
44 | if [[ "${{ matrix.sphinx-version }}" == "dev" ]]; then
45 | SPHINX_INSTALL="git+https://github.com/sphinx-doc/sphinx.git"
46 | else
47 | SPHINX_INSTALL="sphinx==${{ matrix.sphinx-version }}.*"
48 | fi
49 | fi
50 | set -x
51 | python -VV
52 | python -m site
53 | python -m pip install --upgrade pip setuptools wheel
54 | pip install -r requirements_dev.txt $SPHINX_INSTALL
55 | - name: Install Package
56 | run: |
57 | python -m pip install .
58 | - name: Run Tests for ${{ matrix.python-version }}
59 | run: |
60 | python -m tox
61 | vale:
62 | runs-on: ubuntu-latest
63 | steps:
64 | - name: Checkout repository
65 | uses: actions/checkout@v6
66 |
67 | - name: Install docutils
68 | run: sudo apt-get install -y docutils
69 |
70 | - name: Lint with Vale
71 | uses: errata-ai/vale-action@reviewdog
72 | env:
73 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
74 | with:
75 | files: docs
76 | # github-pr-check, github-pr-review, github-check
77 | reporter: github-pr-check
78 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/Passive.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "'%s' may be passive voice. Use active voice if you can."
3 | ignorecase: true
4 | level: warning
5 | raw:
6 | - \b(am|are|were|being|is|been|was|be)\b\s*
7 | tokens:
8 | - '[\w]+ed'
9 | - awoken
10 | - beat
11 | - become
12 | - been
13 | - begun
14 | - bent
15 | - beset
16 | - bet
17 | - bid
18 | - bidden
19 | - bitten
20 | - bled
21 | - blown
22 | - born
23 | - bought
24 | - bound
25 | - bred
26 | - broadcast
27 | - broken
28 | - brought
29 | - built
30 | - burnt
31 | - burst
32 | - cast
33 | - caught
34 | - chosen
35 | - clung
36 | - come
37 | - cost
38 | - crept
39 | - cut
40 | - dealt
41 | - dived
42 | - done
43 | - drawn
44 | - dreamt
45 | - driven
46 | - drunk
47 | - dug
48 | - eaten
49 | - fallen
50 | - fed
51 | - felt
52 | - fit
53 | - fled
54 | - flown
55 | - flung
56 | - forbidden
57 | - foregone
58 | - forgiven
59 | - forgotten
60 | - forsaken
61 | - fought
62 | - found
63 | - frozen
64 | - given
65 | - gone
66 | - gotten
67 | - ground
68 | - grown
69 | - heard
70 | - held
71 | - hidden
72 | - hit
73 | - hung
74 | - hurt
75 | - kept
76 | - knelt
77 | - knit
78 | - known
79 | - laid
80 | - lain
81 | - leapt
82 | - learnt
83 | - led
84 | - left
85 | - lent
86 | - let
87 | - lighted
88 | - lost
89 | - made
90 | - meant
91 | - met
92 | - misspelt
93 | - mistaken
94 | - mown
95 | - overcome
96 | - overdone
97 | - overtaken
98 | - overthrown
99 | - paid
100 | - pled
101 | - proven
102 | - put
103 | - quit
104 | - read
105 | - rid
106 | - ridden
107 | - risen
108 | - run
109 | - rung
110 | - said
111 | - sat
112 | - sawn
113 | - seen
114 | - sent
115 | - set
116 | - sewn
117 | - shaken
118 | - shaven
119 | - shed
120 | - shod
121 | - shone
122 | - shorn
123 | - shot
124 | - shown
125 | - shrunk
126 | - shut
127 | - slain
128 | - slept
129 | - slid
130 | - slit
131 | - slung
132 | - smitten
133 | - sold
134 | - sought
135 | - sown
136 | - sped
137 | - spent
138 | - spilt
139 | - spit
140 | - split
141 | - spoken
142 | - spread
143 | - sprung
144 | - spun
145 | - stolen
146 | - stood
147 | - stridden
148 | - striven
149 | - struck
150 | - strung
151 | - stuck
152 | - stung
153 | - stunk
154 | - sung
155 | - sunk
156 | - swept
157 | - swollen
158 | - sworn
159 | - swum
160 | - swung
161 | - taken
162 | - taught
163 | - thought
164 | - thrived
165 | - thrown
166 | - thrust
167 | - told
168 | - torn
169 | - trodden
170 | - understood
171 | - upheld
172 | - upset
173 | - wed
174 | - wept
175 | - withheld
176 | - withstood
177 | - woken
178 | - won
179 | - worn
180 | - wound
181 | - woven
182 | - written
183 | - wrung
184 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/Weasel.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "'%s' is a weasel word!"
3 | ignorecase: true
4 | level: warning
5 | tokens:
6 | - absolutely
7 | - accidentally
8 | - additionally
9 | - allegedly
10 | - alternatively
11 | - angrily
12 | - anxiously
13 | - approximately
14 | - awkwardly
15 | - badly
16 | - barely
17 | - beautifully
18 | - blindly
19 | - boldly
20 | - bravely
21 | - brightly
22 | - briskly
23 | - bristly
24 | - bubbly
25 | - busily
26 | - calmly
27 | - carefully
28 | - carelessly
29 | - cautiously
30 | - cheerfully
31 | - clearly
32 | - closely
33 | - coldly
34 | - completely
35 | - consequently
36 | - correctly
37 | - courageously
38 | - crinkly
39 | - cruelly
40 | - crumbly
41 | - cuddly
42 | - currently
43 | - daily
44 | - daringly
45 | - deadly
46 | - definitely
47 | - deliberately
48 | - doubtfully
49 | - dumbly
50 | - eagerly
51 | - early
52 | - easily
53 | - elegantly
54 | - enormously
55 | - enthusiastically
56 | - equally
57 | - especially
58 | - eventually
59 | - exactly
60 | - exceedingly
61 | - exclusively
62 | - extremely
63 | - fairly
64 | - faithfully
65 | - fatally
66 | - fiercely
67 | - finally
68 | - fondly
69 | - few
70 | - foolishly
71 | - fortunately
72 | - frankly
73 | - frantically
74 | - generously
75 | - gently
76 | - giggly
77 | - gladly
78 | - gracefully
79 | - greedily
80 | - happily
81 | - hardly
82 | - hastily
83 | - healthily
84 | - heartily
85 | - helpfully
86 | - honestly
87 | - hourly
88 | - hungrily
89 | - hurriedly
90 | - immediately
91 | - impatiently
92 | - inadequately
93 | - ingeniously
94 | - innocently
95 | - inquisitively
96 | - interestingly
97 | - irritably
98 | - jiggly
99 | - joyously
100 | - justly
101 | - kindly
102 | - largely
103 | - lately
104 | - lazily
105 | - likely
106 | - literally
107 | - lonely
108 | - loosely
109 | - loudly
110 | - loudly
111 | - luckily
112 | - madly
113 | - many
114 | - mentally
115 | - mildly
116 | - monthly
117 | - mortally
118 | - mostly
119 | - mysteriously
120 | - neatly
121 | - nervously
122 | - nightly
123 | - noisily
124 | - normally
125 | - obediently
126 | - occasionally
127 | - only
128 | - openly
129 | - painfully
130 | - particularly
131 | - patiently
132 | - perfectly
133 | - politely
134 | - poorly
135 | - powerfully
136 | - presumably
137 | - previously
138 | - promptly
139 | - punctually
140 | - quarterly
141 | - quickly
142 | - quietly
143 | - rapidly
144 | - rarely
145 | - really
146 | - recently
147 | - recklessly
148 | - regularly
149 | - remarkably
150 | - relatively
151 | - reluctantly
152 | - repeatedly
153 | - rightfully
154 | - roughly
155 | - rudely
156 | - sadly
157 | - safely
158 | - selfishly
159 | - sensibly
160 | - seriously
161 | - sharply
162 | - shortly
163 | - shyly
164 | - significantly
165 | - silently
166 | - simply
167 | - sleepily
168 | - slowly
169 | - smartly
170 | - smelly
171 | - smoothly
172 | - softly
173 | - solemnly
174 | - sparkly
175 | - speedily
176 | - stealthily
177 | - sternly
178 | - stupidly
179 | - substantially
180 | - successfully
181 | - suddenly
182 | - surprisingly
183 | - suspiciously
184 | - swiftly
185 | - tenderly
186 | - tensely
187 | - thoughtfully
188 | - tightly
189 | - timely
190 | - truthfully
191 | - unexpectedly
192 | - unfortunately
193 | - usually
194 | - very
195 | - victoriously
196 | - violently
197 | - vivaciously
198 | - warmly
199 | - waverly
200 | - weakly
201 | - wearily
202 | - weekly
203 | - wildly
204 | - wisely
205 | - worldly
206 | - wrinkly
207 | - yearly
208 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/TooWordy.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "'%s' is too wordy."
3 | ignorecase: true
4 | level: warning
5 | tokens:
6 | - a number of
7 | - abundance
8 | - accede to
9 | - accelerate
10 | - accentuate
11 | - accompany
12 | - accomplish
13 | - accorded
14 | - accrue
15 | - acquiesce
16 | - acquire
17 | - additional
18 | - adjacent to
19 | - adjustment
20 | - admissible
21 | - advantageous
22 | - adversely impact
23 | - advise
24 | - aforementioned
25 | - aggregate
26 | - aircraft
27 | - all of
28 | - all things considered
29 | - alleviate
30 | - allocate
31 | - along the lines of
32 | - already existing
33 | - alternatively
34 | - amazing
35 | - ameliorate
36 | - anticipate
37 | - apparent
38 | - appreciable
39 | - as a matter of fact
40 | - as a means of
41 | - as far as I'm concerned
42 | - as of yet
43 | - as to
44 | - as yet
45 | - ascertain
46 | - assistance
47 | - at the present time
48 | - at this time
49 | - attain
50 | - attributable to
51 | - authorize
52 | - because of the fact that
53 | - belated
54 | - benefit from
55 | - bestow
56 | - by means of
57 | - by virtue of
58 | - by virtue of the fact that
59 | - cease
60 | - close proximity
61 | - commence
62 | - comply with
63 | - concerning
64 | - consequently
65 | - consolidate
66 | - constitutes
67 | - demonstrate
68 | - depart
69 | - designate
70 | - discontinue
71 | - due to the fact that
72 | - each and every
73 | - economical
74 | - eliminate
75 | - elucidate
76 | - employ
77 | - endeavor
78 | - enumerate
79 | - equitable
80 | - equivalent
81 | - evaluate
82 | - evidenced
83 | - exclusively
84 | - expedite
85 | - expend
86 | - expiration
87 | - facilitate
88 | - factual evidence
89 | - feasible
90 | - finalize
91 | - first and foremost
92 | - for all intents and purposes
93 | - for the most part
94 | - for the purpose of
95 | - forfeit
96 | - formulate
97 | - have a tendency to
98 | - honest truth
99 | - however
100 | - if and when
101 | - impacted
102 | - implement
103 | - in a manner of speaking
104 | - in a timely manner
105 | - in a very real sense
106 | - in accordance with
107 | - in addition
108 | - in all likelihood
109 | - in an effort to
110 | - in between
111 | - in excess of
112 | - in lieu of
113 | - in light of the fact that
114 | - in many cases
115 | - in my opinion
116 | - in order to
117 | - in regard to
118 | - in some instances
119 | - in terms of
120 | - in the case of
121 | - in the event that
122 | - in the final analysis
123 | - in the nature of
124 | - in the near future
125 | - in the process of
126 | - inception
127 | - incumbent upon
128 | - indicate
129 | - indication
130 | - initiate
131 | - irregardless
132 | - is applicable to
133 | - is authorized to
134 | - is responsible for
135 | - it is
136 | - it is essential
137 | - it seems that
138 | - it was
139 | - magnitude
140 | - maximum
141 | - methodology
142 | - minimize
143 | - minimum
144 | - modify
145 | - monitor
146 | - multiple
147 | - necessitate
148 | - nevertheless
149 | - not certain
150 | - not many
151 | - not often
152 | - not unless
153 | - not unlike
154 | - notwithstanding
155 | - null and void
156 | - numerous
157 | - objective
158 | - obligate
159 | - obtain
160 | - on the contrary
161 | - on the other hand
162 | - one particular
163 | - optimum
164 | - overall
165 | - owing to the fact that
166 | - participate
167 | - particulars
168 | - pass away
169 | - pertaining to
170 | - point in time
171 | - portion
172 | - possess
173 | - preclude
174 | - previously
175 | - prior to
176 | - prioritize
177 | - procure
178 | - proficiency
179 | - provided that
180 | - purchase
181 | - put simply
182 | - readily apparent
183 | - refer back
184 | - regarding
185 | - relocate
186 | - remainder
187 | - remuneration
188 | - requirement
189 | - reside
190 | - residence
191 | - retain
192 | - satisfy
193 | - shall
194 | - should you wish
195 | - similar to
196 | - solicit
197 | - span across
198 | - strategize
199 | - subsequent
200 | - substantial
201 | - successfully complete
202 | - sufficient
203 | - terminate
204 | - the month of
205 | - the point I am trying to make
206 | - therefore
207 | - time period
208 | - took advantage of
209 | - transmit
210 | - transpire
211 | - type of
212 | - until such time as
213 | - utilization
214 | - utilize
215 | - validate
216 | - various different
217 | - what I mean to say is
218 | - whether or not
219 | - with respect to
220 | - with the exception of
221 | - witnessed
222 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | #
2 | # Configuration file for the Sphinx documentation builder.
3 | #
4 | # This file does only contain a selection of the most common options. For a
5 | # full list see the documentation:
6 | # http://www.sphinx-doc.org/en/master/config
7 |
8 | # -- Path setup --------------------------------------------------------------
9 |
10 | # If extensions (or modules to document with autodoc) are in another directory,
11 | # add these directories to sys.path here. If the directory is relative to the
12 | # documentation root, use os.path.abspath to make it absolute, like shown here.
13 | #
14 | import re
15 | import subprocess
16 |
17 | # -- Project information -----------------------------------------------------
18 |
19 | project = "Sphinx Sitemap"
20 | copyright = "Jared Dillard"
21 | author = "Jared Dillard"
22 |
23 | # check if the current commit is tagged as a release (vX.Y.Z)
24 | GIT_TAG_OUTPUT = subprocess.check_output(["git", "tag", "--points-at", "HEAD"])
25 | current_tag = GIT_TAG_OUTPUT.decode().strip()
26 | if re.match(r"^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$", current_tag):
27 | version = current_tag
28 | else:
29 | version = "latest"
30 | # The full version, including alpha/beta/rc tags
31 | release = ""
32 |
33 |
34 | # -- General configuration ---------------------------------------------------
35 |
36 | # If your documentation needs a minimal Sphinx version, state it here.
37 | #
38 | # needs_sphinx = '1.0'
39 |
40 | # Add any Sphinx extension module names here, as strings. They can be
41 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
42 | # ones.
43 | extensions = [
44 | "sphinx_sitemap",
45 | "sphinxemoji.sphinxemoji",
46 | "sphinx_contributors",
47 | "sphinx.ext.intersphinx",
48 | "sphinxext.opengraph",
49 | ]
50 |
51 | # Add any paths that contain templates here, relative to this directory.
52 | templates_path = ["_templates"]
53 |
54 | # The suffix(es) of source filenames.
55 | # You can specify multiple suffix as a list of string:
56 | #
57 | # source_suffix = ['.rst', '.md']
58 | source_suffix = ".rst"
59 |
60 | # The master toctree document.
61 | master_doc = "index"
62 |
63 | # The language for content autogenerated by Sphinx. Refer to documentation
64 | # for a list of supported languages.
65 | #
66 | # This is also used if you do content translation via gettext catalogs.
67 | # Usually you set "language" from the command line for these cases.
68 | language = "en"
69 |
70 | # List of patterns, relative to source directory, that match files and
71 | # directories to ignore when looking for source files.
72 | # This pattern also affects html_static_path and html_extra_path .
73 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
74 |
75 | # The name of the Pygments (syntax highlighting) style to use.
76 | pygments_style = "sphinx"
77 |
78 | intersphinx_mapping = {
79 | "sphinx": ("https://www.sphinx-doc.org/en/master/", None),
80 | }
81 |
82 |
83 | # -- Options for HTML output -------------------------------------------------
84 |
85 | # The theme to use for HTML and HTML Help pages. See the documentation for
86 | # a list of builtin themes.
87 | #
88 | html_theme = "furo"
89 |
90 | # Theme options are theme-specific and customize the look and feel of a theme
91 | # further. For a list of options available for each theme, see the
92 | # documentation.
93 | #
94 | html_theme_options = {}
95 |
96 | # Add any paths that contain custom static files (such as style sheets) here,
97 | # relative to this directory. They are copied after the builtin static files,
98 | # so a file named "default.css" will overwrite the builtin "default.css".
99 | html_static_path = ["_static"]
100 | html_logo = "_static/sitemap-icon.svg"
101 | html_title = "Sphinx Sitemap"
102 |
103 | ogp_site_url = "https://sphinx-sitemap.readthedocs.io/"
104 | ogp_image = "https://sphinx-sitemap.readthedocs.io/en/latest/_static/sitemap-icon.svg"
105 |
106 | # Custom sidebar templates, must be a dictionary that maps document names
107 | # to template names.
108 | #
109 | # The default sidebars (for documents that don't match any pattern) are
110 | # defined by theme itself. Builtin themes are using these templates by
111 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
112 | # 'searchbox.html']``.
113 | #
114 | # html_sidebars = {}
115 |
116 | html_baseurl = "https://sphinx-sitemap.readthedocs.org/"
117 |
118 | sitemap_show_lastmod = True
119 |
120 | # -- Options for HTMLHelp output ---------------------------------------------
121 |
122 | # Output file base name for HTML help builder.
123 | htmlhelp_basename = "SphinxSitemapdoc"
124 |
125 |
126 | # -- Options for LaTeX output ------------------------------------------------
127 |
128 | latex_elements = {
129 | # The paper size ('letterpaper' or 'a4paper').
130 | #
131 | # 'papersize': 'letterpaper',
132 | # The font size ('10pt', '11pt' or '12pt').
133 | #
134 | # 'pointsize': '10pt',
135 | # Additional stuff for the LaTeX preamble.
136 | #
137 | # 'preamble': '',
138 | # Latex figure (float) alignment
139 | #
140 | # 'figure_align': 'htbp',
141 | }
142 |
143 | # Grouping the document tree into LaTeX files. List of tuples
144 | # (source start file, target name, title,
145 | # author, documentclass [howto, manual, or own class]).
146 | latex_documents = [
147 | (
148 | master_doc,
149 | "SphinxSitemap.tex",
150 | "Sphinx Sitemap Documentation",
151 | "Jared Dillard",
152 | "manual",
153 | )
154 | ]
155 |
156 |
157 | # -- Options for manual page output ------------------------------------------
158 |
159 | # One entry per manual page. List of tuples
160 | # (source start file, name, description, authors, manual section).
161 | man_pages = [(master_doc, "sphinxsitemap", "Sphinx Sitemap Documentation", [author], 1)]
162 |
163 |
164 | # -- Options for Texinfo output ----------------------------------------------
165 |
166 | # Grouping the document tree into Texinfo files. List of tuples
167 | # (source start file, target name, title, author,
168 | # dir menu entry, description, category)
169 | texinfo_documents = [
170 | (
171 | master_doc,
172 | "SphinxSitemap",
173 | "Sphinx Sitemap Documentation",
174 | author,
175 | "SphinxSitemap",
176 | "One line description of project.",
177 | "Miscellaneous",
178 | )
179 | ]
180 |
181 |
182 | def setup(app):
183 | app.add_object_type(
184 | "confval",
185 | "confval",
186 | objname="configuration value",
187 | indextemplate="pair: %s; configuration value",
188 | )
189 |
--------------------------------------------------------------------------------
/tests/test_simple.py:
--------------------------------------------------------------------------------
1 | import os
2 | from xml.etree import ElementTree as etree
3 |
4 | import pytest
5 | from git import Repo
6 |
7 |
8 | @pytest.fixture(autouse=True, scope="function")
9 | def git_setup(app):
10 | repo = Repo.init(app.srcdir)
11 | repo.index.add(os.listdir(app.srcdir))
12 | repo.index.commit("test: creating git record for files")
13 | yield
14 |
15 |
16 | @pytest.mark.sphinx(
17 | "html",
18 | freshenv=True,
19 | confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"},
20 | )
21 | def test_simple_html(app, status, warning):
22 | """Tests basic HTML sitemap generation with all pages included."""
23 | app.warningiserror = True
24 | app.build()
25 | assert "sitemap.xml" in os.listdir(app.outdir)
26 | doc = etree.parse(app.outdir / "sitemap.xml")
27 | urls = {
28 | e.text
29 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
30 | }
31 |
32 | assert urls == {
33 | f"https://example.org/docs/en/{d}.html"
34 | for d in [
35 | "index",
36 | "foo",
37 | "bar",
38 | "lorem",
39 | "ipsum",
40 | "dolor",
41 | "elitr",
42 | "genindex",
43 | "search",
44 | ]
45 | }
46 |
47 |
48 | @pytest.mark.sphinx(
49 | "html",
50 | freshenv=True,
51 | confoverrides={
52 | "html_baseurl": "https://example.org/docs/",
53 | "language": "en",
54 | "html_file_suffix": ".htm",
55 | },
56 | )
57 | def test_html_file_suffix(app, status, warning):
58 | """Tests sitemap generation with custom HTML file suffix (.htm)."""
59 | app.warningiserror = True
60 | app.build()
61 | assert "sitemap.xml" in os.listdir(app.outdir)
62 | doc = etree.parse(app.outdir / "sitemap.xml")
63 | urls = {
64 | e.text
65 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
66 | }
67 |
68 | assert urls == {
69 | f"https://example.org/docs/en/{d}.htm"
70 | for d in [
71 | "index",
72 | "foo",
73 | "bar",
74 | "lorem",
75 | "ipsum",
76 | "dolor",
77 | "elitr",
78 | "genindex",
79 | "search",
80 | ]
81 | }
82 |
83 |
84 | @pytest.mark.sphinx(
85 | "dirhtml",
86 | freshenv=True,
87 | confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"},
88 | )
89 | def test_simple_dirhtml(app, status, warning):
90 | """Tests sitemap generation with DirectoryHTMLBuilder (clean URLs)."""
91 | app.warningiserror = True
92 | app.build()
93 | assert "sitemap.xml" in os.listdir(app.outdir)
94 | doc = etree.parse(app.outdir / "sitemap.xml")
95 | urls = {
96 | e.text
97 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
98 | }
99 |
100 | assert urls == {
101 | f"https://example.org/docs/en/{d}"
102 | for d in [
103 | "",
104 | "foo/",
105 | "bar/",
106 | "lorem/",
107 | "ipsum/",
108 | "dolor/",
109 | "elitr/",
110 | "genindex/",
111 | "search/",
112 | ]
113 | }
114 |
115 |
116 | @pytest.mark.sphinx(
117 | "html",
118 | freshenv=True,
119 | confoverrides={
120 | "html_baseurl": "https://example.org/docs/",
121 | "language": "en",
122 | "sitemap_excludes": ["search.html", "genindex.html"],
123 | },
124 | )
125 | def test_simple_excludes(app, status, warning):
126 | """Tests exact string matching for sitemap exclusions (backward compatibility)."""
127 | app.warningiserror = True
128 | app.build()
129 | assert "sitemap.xml" in os.listdir(app.outdir)
130 | doc = etree.parse(app.outdir / "sitemap.xml")
131 | urls = {
132 | e.text
133 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
134 | }
135 |
136 | assert urls == {
137 | f"https://example.org/docs/en/{d}.html"
138 | for d in [
139 | "index",
140 | "foo",
141 | "bar",
142 | "lorem",
143 | "ipsum",
144 | "dolor",
145 | "elitr",
146 | ]
147 | }
148 |
149 |
150 | @pytest.mark.sphinx(
151 | "html",
152 | freshenv=True,
153 | confoverrides={
154 | "html_baseurl": "https://example.org/docs/",
155 | "language": "en",
156 | "sitemap_excludes": ["*index*.html", "search.html"],
157 | },
158 | )
159 | def test_wildcard_excludes(app, status, warning):
160 | """Tests that *index*.html wildcard pattern excludes both "index.html" and "genindex.html"."""
161 | app.warningiserror = True
162 | app.build()
163 | assert "sitemap.xml" in os.listdir(app.outdir)
164 | doc = etree.parse(app.outdir / "sitemap.xml")
165 | urls = {
166 | e.text
167 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
168 | }
169 |
170 | # *index*.html should exclude both "genindex.html" and "index.html"
171 | assert urls == {
172 | f"https://example.org/docs/en/{d}.html"
173 | for d in [
174 | "foo",
175 | "bar",
176 | "lorem",
177 | "ipsum",
178 | "dolor",
179 | "elitr",
180 | ]
181 | }
182 |
183 |
184 | @pytest.mark.sphinx(
185 | "html",
186 | freshenv=True,
187 | confoverrides={
188 | "html_baseurl": "https://example.org/docs/",
189 | "language": "en",
190 | "sitemap_excludes": ["l*.html"], # Excludes lorem.html but not other files
191 | },
192 | )
193 | def test_pattern_excludes(app, status, warning):
194 | """Tests that l*.html wildcard pattern excludes only "lorem.html"."""
195 | app.warningiserror = True
196 | app.build()
197 | assert "sitemap.xml" in os.listdir(app.outdir)
198 | doc = etree.parse(app.outdir / "sitemap.xml")
199 | urls = {
200 | e.text
201 | for e in doc.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}loc")
202 | }
203 |
204 | # l*.html should exclude "lorem.html"
205 | assert urls == {
206 | f"https://example.org/docs/en/{d}.html"
207 | for d in [
208 | "index",
209 | "foo",
210 | "bar",
211 | "ipsum",
212 | "dolor",
213 | "elitr",
214 | "genindex",
215 | "search",
216 | ]
217 | }
218 |
219 |
220 | @pytest.mark.sphinx(
221 | "html",
222 | freshenv=True,
223 | confoverrides={
224 | "html_baseurl": "https://example.org/docs/",
225 | "language": "en",
226 | "sitemap_indent": 2,
227 | },
228 | )
229 | def test_indent(app, status, warning):
230 | """Tests that xml output is indented"""
231 | app.warningiserror = True
232 | app.build()
233 | assert "sitemap.xml" in os.listdir(app.outdir)
234 | with open(app.outdir / "sitemap.xml", "r") as fd:
235 | lines = fd.readlines()
236 |
237 | assert lines[0][0] == "<"
238 | assert lines[1][0] == "<"
239 | assert lines[2][0:3] == " <"
240 |
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | .. vale off
2 |
3 | Changelog
4 | =========
5 |
6 | 2.9.0
7 | -----
8 |
9 | - |:sparkles:| NEW: Add :confval:`sitemap_indent` configuration value to control XML indentation
10 | `#112 `_
11 |
12 | 2.8.0
13 | -----
14 |
15 | - |:sparkles:| NEW: Add support for wildcard patterns to :confval:`sitemap_excludes`
16 | `#113 `_
17 |
18 | 2.7.2
19 | -----
20 |
21 | *Release date: 2025-06-26*
22 |
23 | - |:bug:| FIX: Change :confval:`sitemap_show_lastmod` to default of ``False``
24 |
25 | 2.7.1
26 | -----
27 |
28 | *Release date: 2025-06-20*
29 |
30 | - Remove support for Python 3.8
31 |
32 | 2.7.0
33 | -----
34 |
35 | *Release date: 2025-06-20*
36 |
37 | * |:sparkles:| NEW: Add support for ``lastmod`` using `sphinx-last-updated-by-git`_
38 | `#95 `_
39 |
40 | 2.6.0
41 | -----
42 |
43 | *Release date: 2024-04-28*
44 |
45 | * |:wrench:| MAINT: Fix deprecated sphinx.testing.path
46 | `#83 `_
47 | * Drop test support for Python 3.7 and Sphinx 2, 3, and 4.
48 | * |:sparkles:| NEW: Add sitemap_excludes configuration
49 | `#91 `_
50 |
51 | 2.5.1
52 | -----
53 |
54 | *Release date: 2023-08-17*
55 |
56 | * |:bug:| FIX: Fix Path use for Sphinx 7.2
57 | `#70 `_
58 | * |:bug:| FIX: Fix incremental building by preventing multiprocessing queue from being pickled with environment
59 | `#62 `_
60 | * |:wrench:| MAINT: Add docstrings and type hints
61 | `#61 `_
62 |
63 | 2.5.0
64 | -----
65 |
66 | *Release date: 2023-01-28*
67 |
68 | * |:books:| DOCS: Calculate version for sitemap based on current tag
69 | `#53 `_
70 | * |:test_tube:| TESTS: Add Sphinx 6 env to tox
71 | `#55 `_
72 | * |:sparkles:| NEW: Add support for Sphinx config "html_file_suffix"
73 | `#57 `_
74 | * |:books:| DOCS: Add site search optimization
75 | `#58 `_
76 |
77 | 2.4.0
78 | -----
79 |
80 | *Release date: 2022-12-26*
81 |
82 | * |:books:| DOCS: Add ReadTheDocs docs
83 | `#45 `_
84 | * |:wrench:| MAINT: General code clean up
85 | `#46 `_
86 | * |:sparkles:| NEW: Add support for parallel mode
87 | `#47 `_
88 | * |:test_tube:| TESTS: Add tests for ``dirhtml`` builder
89 | `#48 `_
90 | * |:test_tube:| TESTS: Add vale support for docs
91 | `#49 `_
92 | * |:bug:| FIX: Fix wheel includes so they don't include docs and tests
93 | `#51 `_
94 | * |:books:| DOCS: Add write-good and improve writing
95 | `#52 `_
96 |
97 | 2.3.0
98 | -----
99 |
100 | *Release date: 2022-12-21*
101 |
102 | * |:wrench:| MAINT: Clean up how package versions are handled
103 | * |:test_tube:| TESTS: Install pre-commit with ``isort``, ``black``, and ``flake8``
104 | `#35 `_
105 | * |:books:| DOCS: Improve the wording of the README to help with issues upgrading to Sphinx 5
106 | `#36 `_
107 | * |:bug:| FIX: Follow correct format for multilingual sitemaps
108 | `#38 `_
109 | * |:wrench:| MAINT: Update the build process
110 | `#39 `_
111 | * |:test_tube:| TESTS: Add testing infrastructure
112 | `#41 `_
113 | `#42 `_
114 | * |:wrench:| MAINT: Use logging for all logging messages
115 | `#40 `_
116 |
117 | 2.2.1
118 | -----
119 |
120 | *Release date: 2022-11-11*
121 |
122 | * |:books:| DOCS: Fix :confval:`sitemap_url_scheme` default value in **README** file
123 | `#32 `_
124 | * |:wrench:| MAINT: Clean up package classifiers
125 | * |:wrench:| MAINT: Add **LICENSE** to source distributions
126 | `#27 `_
127 | * |:books:| DOCS: Add Conda Forge badge to **README** file
128 |
129 | 2.2.0
130 | ------
131 |
132 | *Release date: 2020-08-05*
133 |
134 | * |:wrench:| MAINT: Add ``parallel_write_safe`` flag and set to `False`
135 | `#20 `_.
136 | * |:sparkles:| Add :confval:`sitemap_locales` that creates an allow list of locales
137 | `#25 `_.
138 | * |:sparkles:| Add :confval:`sitemap_filename` that allows for custom sitemap name
139 | `#26 `_.
140 |
141 | 2.1.0
142 | -----
143 |
144 | *Release date: 2020-02-22*
145 |
146 | * |:bug:| FIX: Make sure the regional variants for the ``hreflang`` attribute are valid
147 | `#19 `_.
148 |
149 | 2.0.0
150 | -----
151 |
152 | *Release date: 2020-02-19*
153 |
154 | * |:sparkles:| NEW: Add :confval:`sitemap_url_scheme` that allows the URL scheme to be customized with a default of ``{version}{lang}{link}``
155 | `#22 `_.
156 |
157 | .. note:: This has the potential to be a breaking change depending on how the ``version`` and ``language`` values are set. **Confirm the accuracy of the sitemap after upgrading**.
158 |
159 | 1.1.0
160 | -----
161 |
162 | *Release date: 2019-12-12*
163 |
164 | * |:sparkles:| NEW: Add support for ``DirectoryHTMLBuilder``.
165 | * |:wrench:| MAINT: Remove unused ``HTMLTranslator`` import.
166 | * |:sparkles:| NEW: Make ``version`` and ``language`` each optional.
167 | * |:wrench:| MAINT: Add license to **setup.py**.
168 | * |:wrench:| MAINT: Mark unsafe for parallel reading.
169 |
170 | 1.0.2
171 | -----
172 |
173 | *Release date: 2019-02-09*
174 |
175 | * |:wrench:| MAINT: Add ``html_baseurl`` if it doesn't exist for sphinx versions prior to 1.8.0.
176 |
177 | 1.0.1
178 | -----
179 |
180 | *Release date: 2019-01-17*
181 |
182 | * |:bug:| FIX: Fix for ``AttributeError: No such config value: html_baseurl`` on versions of sphinx older than 1.8.0.
183 |
184 | 1.0.0
185 | -----
186 |
187 | *Release date: 2019-01-17*
188 |
189 | * |:wrench:| MAINT: Use native ``html_baseurl``, instead of the custom ``site_url``. It checks for both for backwards compatibility.
190 | * |:sparkles:| NEW: Add support for multiple languages.
191 |
192 | 0.3.1
193 | -----
194 |
195 | *Release date: 2018-03-04*
196 |
197 | * |:books:| DOCS: Add instructions on maintaining PyPI version to the docs
198 |
199 | 0.3.0
200 | -----
201 |
202 | *Release date: 2018-03-04*
203 |
204 | * |:wrench:| MAINT: Remove unnecessary ``HTMLTranslator``.
205 | * |:books:| DOCS: Improve documentation
206 |
207 | 0.2
208 | ---
209 |
210 | *Release date: 2017-11-28*
211 |
212 | * |:wrench:| MAINT: Fix PyPI description
213 |
214 | 0.1
215 | ---
216 |
217 | *Release date: 2017-11-28*
218 |
219 | * Initial Release |:tada:|
220 |
221 |
222 | .. _sphinx-last-updated-by-git: https://pypi.org/project/sphinx-last-updated-by-git/
223 |
--------------------------------------------------------------------------------
/docs/source/advanced-configuration.rst:
--------------------------------------------------------------------------------
1 | Advanced Configuration
2 | ======================
3 |
4 | .. _configuration_customizing_url_scheme:
5 |
6 | Customizing the URL Scheme
7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
8 |
9 | :confval:`sitemap_url_scheme` defaults to ``{lang}{version}{link}``, where ``{lang}`` and ``{version}`` get set by :confval:`language` and :confval:`version` in **conf.py**.
10 |
11 | .. important:: As of Sphinx version 5, ``language`` defaults to ``"en"``, if that
12 | makes the default scheme produce the incorrect URL, then change the default behavior.
13 |
14 | To change the default behavior, set the value of :confval:`sitemap_url_scheme` in **conf.py** to the
15 | desired format. For example:
16 |
17 | .. code-block:: python
18 |
19 | sitemap_url_scheme = "{link}"
20 |
21 | Or for nested deployments, something like:
22 |
23 | .. code-block:: python
24 |
25 | sitemap_url_scheme = "{version}{lang}subdir/{link}"
26 |
27 | .. note:: The extension automatically appends trailing slashes to both the ``language`` and ``version`` values.
28 | You can also omit values from the scheme for desired behavior.
29 |
30 |
31 | .. _configuration_changing_filename:
32 |
33 | Changing the Filename
34 | ^^^^^^^^^^^^^^^^^^^^^
35 |
36 | Set :confval:`sitemap_filename` in **conf.py** to the desired filename, for example:
37 |
38 | .. code-block:: python
39 |
40 | sitemap_filename = "sitemap.xml"
41 |
42 | Version Support
43 | ^^^^^^^^^^^^^^^
44 |
45 | :confval:`version` specifies the version of the sitemap.
46 | For multi-version sitemaps, generate a sitemap per version and then manually add each to a `sitemapindex.xml`_ file.
47 |
48 | Tagged Releases
49 | ~~~~~~~~~~~~~~~
50 |
51 | For a tagged release deploy strategy where the ``latest`` gets created from head of the branch and versions get created from tagged commits, check to see if the current commit matches the release tag regex and set :confval:`version` accordingly.
52 |
53 | .. code-block:: python
54 |
55 | # check if the current commit is tagged as a release (vX.Y.Z) and set the version
56 | GIT_TAG_OUTPUT = subprocess.check_output(["git", "tag", "--points-at", "HEAD"])
57 | current_tag = GIT_TAG_OUTPUT.decode().strip()
58 | if re.match(r"^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$", current_tag):
59 | version = current_tag
60 | else:
61 | version = "latest"
62 |
63 | .. tip:: Set the canonical URL in the theme layout of all versions to the latest version of that page, for example:
64 |
65 | .. code-block:: html
66 |
67 |
68 |
69 | .. _configuration_supporting_multiple_languages:
70 |
71 | Language Support
72 | ^^^^^^^^^^^^^^^^
73 |
74 | :confval:`language` specifies the primary language. Any alternative languages get detected using the contents of :confval:`locale_dirs`.
75 |
76 | For example, with a primary language of **en**, and **es** and **fr** as detected translations, the sitemap look like this:
77 |
78 | .. code-block:: xml
79 |
80 |
81 |
82 |
83 | https://my-site.com/docs/en/index.html
84 |
85 |
86 |
87 |
88 |
89 | https://my-site.com/docs/en/about.html
90 |
91 |
92 |
93 |
94 |
95 |
96 | Use :confval:`sitemap_locales` to manually specify a list of locales to include in the sitemap:
97 |
98 | .. code-block:: python
99 |
100 | sitemap_locales = ['en', 'es']
101 |
102 | The end result looks something like the following for each language/version build:
103 |
104 | .. code-block:: xml
105 |
106 |
107 |
108 |
109 | https://my-site.com/docs/en/index.html
110 |
111 |
112 |
113 |
114 | https://my-site.com/docs/en/about.html
115 |
116 |
117 |
118 |
119 |
120 | To generate the primary language with no alternatives, set :confval:`sitemap_locales` to ``[None]``:
121 |
122 | .. code-block:: python
123 |
124 | sitemap_locales = [None]
125 |
126 | For multilingual sitemaps, generate a sitemap per language and then manually add each to a `sitemapindex.xml`_ file.
127 |
128 | .. _configuration_excluding_pages:
129 |
130 | Excluding Pages
131 | ^^^^^^^^^^^^^^^
132 |
133 | To exclude a set of pages, add each page's path to ``sitemap_excludes``.
134 | You can use exact paths or wildcard patterns:
135 |
136 | .. code-block:: python
137 |
138 | sitemap_excludes = [
139 | "search.html", # Exact match
140 | "genindex.html", # Exact match
141 | "modules/*", # Wildcard pattern - matches files starting with "_modules/"
142 | ]
143 |
144 | Unix-style wildcards are supported:
145 |
146 | - ``*`` matches any number of characters
147 | - ``?`` matches any single character
148 | - ``[seq]`` matches any character in seq
149 | - ``[!seq]`` matches any character not in seq
150 |
151 | .. _configuration_lastmod:
152 |
153 | Adding Last Modified Timestamps
154 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
155 |
156 | To enable last modified timestamps in your sitemap, set :confval:`sitemap_show_lastmod` to ``True`` in **conf.py**:
157 |
158 | .. code-block:: python
159 |
160 | sitemap_show_lastmod = True
161 |
162 | When enabled, the extension uses Git to determine the last modified date for each page based on the most recent commit that modified the source file.
163 | This produces sitemap entries like:
164 |
165 | .. code-block:: xml
166 |
167 |
168 | https://my-site.com/docs/en/index.html
169 | 2024-01-15T10:30:00+00:00
170 |
171 |
172 | .. important::
173 |
174 | This feature requires Git to be available and your documentation to be in a Git repository.
175 | If Git is not available or the file is not tracked, no ```` element will be added for that page.
176 | Shallow clones, which is the default for GitHub Actions, are not supported at this time.
177 |
178 | .. tip:: The ```` timestamps are particularly useful for :ref:`RAG (Retrieval-Augmented Generation) systems ` that need to identify recently updated content for incremental updates.
179 |
180 |
181 | .. _sitemapindex.xml: https://support.google.com/webmasters/answer/75712?hl=en
182 | .. _sitemaps.org: https://www.sitemaps.org/protocol.html
183 |
184 | .. _configuration_indent:
185 |
186 | Formatting XML Output
187 | ^^^^^^^^^^^^^^^^^^^^^
188 |
189 | To add indention to the XML output, set :confval:`sitemap_indent` to the number of spaces for indentation in **conf.py**:
190 |
191 | .. code-block:: python
192 |
193 | sitemap_indent = 2
194 |
195 | Set to ``0`` (the default) to disable indentation:
196 |
197 | .. code-block:: python
198 |
199 | sitemap_indent = 0
200 |
--------------------------------------------------------------------------------
/sphinx_sitemap/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2013 Michael Dowling
2 | # Copyright (c) 2017 Jared Dillard
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy
5 | # of this software and associated documentation files (the "Software"), to deal
6 | # in the Software without restriction, including without limitation the rights
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | # copies of the Software, and to permit persons to whom the Software is
9 | # furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 |
14 | import fnmatch
15 | import os
16 | import queue
17 | from datetime import datetime, timezone
18 | from multiprocessing import Manager
19 | from pathlib import Path
20 | from typing import Any, Dict, List, Optional
21 | from xml.etree import ElementTree
22 |
23 | from sphinx.application import Sphinx
24 | from sphinx.errors import ExtensionError
25 | from sphinx.util.logging import getLogger
26 |
27 | __version__ = "2.9.0"
28 |
29 | logger = getLogger(__name__)
30 |
31 |
32 | def setup(app: Sphinx) -> Dict[str, Any]:
33 | """
34 | Sphinx extension setup function.
35 | It adds config values and connects Sphinx events to the sitemap builder.
36 |
37 | :param app: The Sphinx Application instance
38 | :return: A dict of Sphinx extension options
39 | """
40 | app.add_config_value("site_url", default=None, rebuild="")
41 | app.add_config_value(
42 | "sitemap_url_scheme", default="{lang}{version}{link}", rebuild=""
43 | )
44 | app.add_config_value("sitemap_locales", default=[], rebuild="")
45 |
46 | app.add_config_value("sitemap_filename", default="sitemap.xml", rebuild="")
47 |
48 | app.add_config_value("sitemap_excludes", default=[], rebuild="")
49 |
50 | app.add_config_value("sitemap_show_lastmod", default=False, rebuild="")
51 |
52 | app.add_config_value("sitemap_indent", default=0, rebuild="")
53 |
54 | try:
55 | app.add_config_value("html_baseurl", default=None, rebuild="")
56 | except BaseException:
57 | pass
58 |
59 | # install sphinx_last_updated_by_git extension if it exists
60 | if app.config.sitemap_show_lastmod:
61 | try:
62 | app.setup_extension("sphinx_last_updated_by_git")
63 | except ExtensionError as e:
64 | logger.warning(
65 | f"{e}",
66 | type="sitemap",
67 | subtype="configuration",
68 | )
69 | app.config.sitemap_show_lastmod = False
70 |
71 | app.connect("builder-inited", record_builder_type)
72 | app.connect("html-page-context", add_html_link)
73 | app.connect("build-finished", create_sitemap)
74 |
75 | return {
76 | "parallel_read_safe": True,
77 | "parallel_write_safe": True,
78 | "version": __version__,
79 | }
80 |
81 |
82 | def get_locales(app: Sphinx) -> List[str]:
83 | """
84 | Get a list of locales from the extension config or automatically detect based
85 | on Sphinx Application config.
86 |
87 | :param app: The Sphinx Application instance
88 | :return: A list of locales
89 | """
90 | # Manually configured list of locales
91 | sitemap_locales: Optional[List[str]] = app.builder.config.sitemap_locales
92 | if sitemap_locales:
93 | # special value to add nothing -> use primary language only
94 | if sitemap_locales == [None]:
95 | return []
96 |
97 | # otherwise, add each locale
98 | return [locale for locale in sitemap_locales]
99 |
100 | # Or autodetect locales
101 | locales = []
102 | for locale_dir in app.builder.config.locale_dirs:
103 | locale_dir = os.path.join(app.confdir, locale_dir)
104 | if os.path.isdir(locale_dir):
105 | for locale in os.listdir(locale_dir):
106 | if os.path.isdir(os.path.join(locale_dir, locale)):
107 | locales.append(locale)
108 | return locales
109 |
110 |
111 | def record_builder_type(app: Sphinx):
112 | """
113 | Determine if the Sphinx Builder is an instance of DirectoryHTMLBuilder and store that in the
114 | application environment.
115 |
116 | :param app: The Sphinx Application instance
117 | """
118 | # builder isn't initialized in the setup so we do it here
119 | builder = getattr(app, "builder", None)
120 | if builder is None:
121 | return
122 | builder.env.is_directory_builder = type(builder).__name__ == "DirectoryHTMLBuilder"
123 | builder.env.app.sitemap_links = Manager().Queue()
124 |
125 |
126 | def is_excluded(sitemap_link: str, exclude_patterns: List[str]) -> bool:
127 | """
128 | Check if a sitemap link should be excluded based on wildcard patterns.
129 |
130 | :param sitemap_link: The sitemap link to check
131 | :param exclude_patterns: List of wildcard patterns to match against
132 | :return: True if the link matches any exclude pattern, False otherwise
133 | """
134 | return any(fnmatch.fnmatch(sitemap_link, pattern) for pattern in exclude_patterns)
135 |
136 |
137 | def hreflang_formatter(lang: str) -> str:
138 | """
139 | Format the supplied locale code into a string that is compatible with `hreflang`.
140 | See also:
141 |
142 | - https://en.wikipedia.org/wiki/Hreflang#Common_Mistakes
143 | - https://github.com/readthedocs/readthedocs.org/pull/5638
144 |
145 | :param lang: The locale string to format
146 | :return: The formatted locale string
147 | """
148 | if "_" in lang:
149 | return lang.replace("_", "-")
150 | return lang
151 |
152 |
153 | def add_html_link(app: Sphinx, pagename: str, templatename, context, doctree):
154 | """
155 | As each page is built, collect page names for the sitemap
156 |
157 | :param app: The Sphinx Application instance
158 | :param pagename: The current page being built
159 | """
160 | env = app.builder.env
161 | if app.builder.config.html_file_suffix is None:
162 | file_suffix = ".html"
163 | else:
164 | file_suffix = app.builder.config.html_file_suffix
165 |
166 | last_updated = None
167 | if app.builder.config.sitemap_show_lastmod and pagename in env.git_last_updated:
168 | timestamp, show_sourcelink = env.git_last_updated[pagename]
169 | # TODO verify dates
170 | # TODO handle untracked pages (add option to use current timestamp?)
171 | if timestamp:
172 | utc_date = datetime.fromtimestamp(int(timestamp), timezone.utc)
173 | last_updated = utc_date.strftime("%Y-%m-%dT%H:%M:%SZ")
174 |
175 | # Support DirectoryHTMLBuilder path structure
176 | # where generated links between pages omit the index.html
177 | if env.is_directory_builder: # type: ignore
178 | if pagename == "index":
179 | sitemap_link = ""
180 | elif pagename.endswith("/index"):
181 | sitemap_link = pagename[:-6] + "/"
182 | else:
183 | sitemap_link = pagename + "/"
184 | else:
185 | sitemap_link = pagename + file_suffix
186 |
187 | if not is_excluded(sitemap_link, app.builder.config.sitemap_excludes):
188 | env.app.sitemap_links.put((sitemap_link, last_updated)) # type: ignore
189 |
190 |
191 | def create_sitemap(app: Sphinx, exception):
192 | """
193 | Generates the sitemap.xml from the collected HTML page links.
194 |
195 | :param app: The Sphinx Application instance
196 | """
197 | site_url = app.builder.config.site_url or app.builder.config.html_baseurl
198 | if site_url:
199 | site_url.rstrip("/") + "/"
200 | else:
201 | logger.warning(
202 | "sphinx-sitemap: html_baseurl is required in conf.py." "Sitemap not built.",
203 | type="sitemap",
204 | subtype="configuration",
205 | )
206 | return
207 |
208 | if app.env.app.sitemap_links.empty(): # type: ignore
209 | logger.info(
210 | "sphinx-sitemap: No pages generated for %s" % app.config.sitemap_filename,
211 | type="sitemap",
212 | subtype="information",
213 | )
214 | return
215 |
216 | ElementTree.register_namespace("xhtml", "http://www.w3.org/1999/xhtml")
217 |
218 | root = ElementTree.Element(
219 | "urlset", xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
220 | )
221 |
222 | locales = get_locales(app)
223 |
224 | if app.builder.config.version:
225 | version = app.builder.config.version + "/"
226 | else:
227 | version = ""
228 |
229 | while True:
230 | try:
231 | link, last_updated = app.env.app.sitemap_links.get_nowait() # type: ignore
232 | except queue.Empty:
233 | break
234 |
235 | url = ElementTree.SubElement(root, "url")
236 | scheme = app.config.sitemap_url_scheme
237 | if app.builder.config.language:
238 | lang = app.builder.config.language + "/"
239 | else:
240 | lang = ""
241 |
242 | # add page url
243 | ElementTree.SubElement(url, "loc").text = site_url + scheme.format(
244 | lang=lang, version=version, link=link
245 | )
246 |
247 | # add page lastmode date if it exists
248 | if last_updated:
249 | ElementTree.SubElement(url, "lastmod").text = last_updated
250 |
251 | # add alternate language page urls
252 | for lang in locales:
253 | lang = lang + "/"
254 | ElementTree.SubElement(
255 | url,
256 | "{http://www.w3.org/1999/xhtml}link",
257 | rel="alternate",
258 | hreflang=hreflang_formatter(lang.rstrip("/")),
259 | href=site_url + scheme.format(lang=lang, version=version, link=link),
260 | )
261 |
262 | filename = Path(app.outdir) / app.config.sitemap_filename
263 | if isinstance(app.config.sitemap_indent, int) and app.config.sitemap_indent > 0:
264 | ElementTree.indent(root, space=app.config.sitemap_indent * " ")
265 |
266 | ElementTree.ElementTree(root).write(
267 | filename, xml_declaration=True, encoding="utf-8", method="xml"
268 | )
269 |
270 | logger.info(
271 | "sphinx-sitemap: %s was generated for URL %s in %s"
272 | % (app.config.sitemap_filename, site_url, filename),
273 | type="sitemap",
274 | subtype="information",
275 | )
276 |
--------------------------------------------------------------------------------
/docs/_vale/write-good/Cliches.yml:
--------------------------------------------------------------------------------
1 | extends: existence
2 | message: "Try to avoid using clichés like '%s'."
3 | ignorecase: true
4 | level: warning
5 | tokens:
6 | - a chip off the old block
7 | - a clean slate
8 | - a dark and stormy night
9 | - a far cry
10 | - a fine kettle of fish
11 | - a loose cannon
12 | - a penny saved is a penny earned
13 | - a tough row to hoe
14 | - a word to the wise
15 | - ace in the hole
16 | - acid test
17 | - add insult to injury
18 | - against all odds
19 | - air your dirty laundry
20 | - all fun and games
21 | - all in a day's work
22 | - all talk, no action
23 | - all thumbs
24 | - all your eggs in one basket
25 | - all's fair in love and war
26 | - all's well that ends well
27 | - almighty dollar
28 | - American as apple pie
29 | - an axe to grind
30 | - another day, another dollar
31 | - armed to the teeth
32 | - as luck would have it
33 | - as old as time
34 | - as the crow flies
35 | - at loose ends
36 | - at my wits end
37 | - avoid like the plague
38 | - babe in the woods
39 | - back against the wall
40 | - back in the saddle
41 | - back to square one
42 | - back to the drawing board
43 | - bad to the bone
44 | - badge of honor
45 | - bald faced liar
46 | - ballpark figure
47 | - banging your head against a brick wall
48 | - baptism by fire
49 | - barking up the wrong tree
50 | - bat out of hell
51 | - be all and end all
52 | - beat a dead horse
53 | - beat around the bush
54 | - been there, done that
55 | - beggars can't be choosers
56 | - behind the eight ball
57 | - bend over backwards
58 | - benefit of the doubt
59 | - bent out of shape
60 | - best thing since sliced bread
61 | - bet your bottom dollar
62 | - better half
63 | - better late than never
64 | - better mousetrap
65 | - better safe than sorry
66 | - between a rock and a hard place
67 | - beyond the pale
68 | - bide your time
69 | - big as life
70 | - big cheese
71 | - big fish in a small pond
72 | - big man on campus
73 | - bigger they are the harder they fall
74 | - bird in the hand
75 | - bird's eye view
76 | - birds and the bees
77 | - birds of a feather flock together
78 | - bit the hand that feeds you
79 | - bite the bullet
80 | - bite the dust
81 | - bitten off more than he can chew
82 | - black as coal
83 | - black as pitch
84 | - black as the ace of spades
85 | - blast from the past
86 | - bleeding heart
87 | - blessing in disguise
88 | - blind ambition
89 | - blind as a bat
90 | - blind leading the blind
91 | - blood is thicker than water
92 | - blood sweat and tears
93 | - blow off steam
94 | - blow your own horn
95 | - blushing bride
96 | - boils down to
97 | - bolt from the blue
98 | - bone to pick
99 | - bored stiff
100 | - bored to tears
101 | - bottomless pit
102 | - boys will be boys
103 | - bright and early
104 | - brings home the bacon
105 | - broad across the beam
106 | - broken record
107 | - brought back to reality
108 | - bull by the horns
109 | - bull in a china shop
110 | - burn the midnight oil
111 | - burning question
112 | - burning the candle at both ends
113 | - burst your bubble
114 | - bury the hatchet
115 | - busy as a bee
116 | - by hook or by crook
117 | - call a spade a spade
118 | - called onto the carpet
119 | - calm before the storm
120 | - can of worms
121 | - can't cut the mustard
122 | - can't hold a candle to
123 | - case of mistaken identity
124 | - cat got your tongue
125 | - cat's meow
126 | - caught in the crossfire
127 | - caught red-handed
128 | - checkered past
129 | - chomping at the bit
130 | - cleanliness is next to godliness
131 | - clear as a bell
132 | - clear as mud
133 | - close to the vest
134 | - cock and bull story
135 | - cold shoulder
136 | - come hell or high water
137 | - cool as a cucumber
138 | - cool, calm, and collected
139 | - cost a king's ransom
140 | - count your blessings
141 | - crack of dawn
142 | - crash course
143 | - creature comforts
144 | - cross that bridge when you come to it
145 | - crushing blow
146 | - cry like a baby
147 | - cry me a river
148 | - cry over spilt milk
149 | - crystal clear
150 | - curiosity killed the cat
151 | - cut and dried
152 | - cut through the red tape
153 | - cut to the chase
154 | - cute as a bugs ear
155 | - cute as a button
156 | - cute as a puppy
157 | - cuts to the quick
158 | - dark before the dawn
159 | - day in, day out
160 | - dead as a doornail
161 | - devil is in the details
162 | - dime a dozen
163 | - divide and conquer
164 | - dog and pony show
165 | - dog days
166 | - dog eat dog
167 | - dog tired
168 | - don't burn your bridges
169 | - don't count your chickens
170 | - don't look a gift horse in the mouth
171 | - don't rock the boat
172 | - don't step on anyone's toes
173 | - don't take any wooden nickels
174 | - down and out
175 | - down at the heels
176 | - down in the dumps
177 | - down the hatch
178 | - down to earth
179 | - draw the line
180 | - dressed to kill
181 | - dressed to the nines
182 | - drives me up the wall
183 | - dull as dishwater
184 | - dyed in the wool
185 | - eagle eye
186 | - ear to the ground
187 | - early bird catches the worm
188 | - easier said than done
189 | - easy as pie
190 | - eat your heart out
191 | - eat your words
192 | - eleventh hour
193 | - even the playing field
194 | - every dog has its day
195 | - every fiber of my being
196 | - everything but the kitchen sink
197 | - eye for an eye
198 | - face the music
199 | - facts of life
200 | - fair weather friend
201 | - fall by the wayside
202 | - fan the flames
203 | - feast or famine
204 | - feather your nest
205 | - feathered friends
206 | - few and far between
207 | - fifteen minutes of fame
208 | - filthy vermin
209 | - fine kettle of fish
210 | - fish out of water
211 | - fishing for a compliment
212 | - fit as a fiddle
213 | - fit the bill
214 | - fit to be tied
215 | - flash in the pan
216 | - flat as a pancake
217 | - flip your lid
218 | - flog a dead horse
219 | - fly by night
220 | - fly the coop
221 | - follow your heart
222 | - for all intents and purposes
223 | - for the birds
224 | - for what it's worth
225 | - force of nature
226 | - force to be reckoned with
227 | - forgive and forget
228 | - fox in the henhouse
229 | - free and easy
230 | - free as a bird
231 | - fresh as a daisy
232 | - full steam ahead
233 | - fun in the sun
234 | - garbage in, garbage out
235 | - gentle as a lamb
236 | - get a kick out of
237 | - get a leg up
238 | - get down and dirty
239 | - get the lead out
240 | - get to the bottom of
241 | - get your feet wet
242 | - gets my goat
243 | - gilding the lily
244 | - give and take
245 | - go against the grain
246 | - go at it tooth and nail
247 | - go for broke
248 | - go him one better
249 | - go the extra mile
250 | - go with the flow
251 | - goes without saying
252 | - good as gold
253 | - good deed for the day
254 | - good things come to those who wait
255 | - good time was had by all
256 | - good times were had by all
257 | - greased lightning
258 | - greek to me
259 | - green thumb
260 | - green-eyed monster
261 | - grist for the mill
262 | - growing like a weed
263 | - hair of the dog
264 | - hand to mouth
265 | - happy as a clam
266 | - happy as a lark
267 | - hasn't a clue
268 | - have a nice day
269 | - have high hopes
270 | - have the last laugh
271 | - haven't got a row to hoe
272 | - head honcho
273 | - head over heels
274 | - hear a pin drop
275 | - heard it through the grapevine
276 | - heart's content
277 | - heavy as lead
278 | - hem and haw
279 | - high and dry
280 | - high and mighty
281 | - high as a kite
282 | - hit paydirt
283 | - hold your head up high
284 | - hold your horses
285 | - hold your own
286 | - hold your tongue
287 | - honest as the day is long
288 | - horns of a dilemma
289 | - horse of a different color
290 | - hot under the collar
291 | - hour of need
292 | - I beg to differ
293 | - icing on the cake
294 | - if the shoe fits
295 | - if the shoe were on the other foot
296 | - in a jam
297 | - in a jiffy
298 | - in a nutshell
299 | - in a pig's eye
300 | - in a pinch
301 | - in a word
302 | - in hot water
303 | - in the gutter
304 | - in the nick of time
305 | - in the thick of it
306 | - in your dreams
307 | - it ain't over till the fat lady sings
308 | - it goes without saying
309 | - it takes all kinds
310 | - it takes one to know one
311 | - it's a small world
312 | - it's only a matter of time
313 | - ivory tower
314 | - Jack of all trades
315 | - jockey for position
316 | - jog your memory
317 | - joined at the hip
318 | - judge a book by its cover
319 | - jump down your throat
320 | - jump in with both feet
321 | - jump on the bandwagon
322 | - jump the gun
323 | - jump to conclusions
324 | - just a hop, skip, and a jump
325 | - just the ticket
326 | - justice is blind
327 | - keep a stiff upper lip
328 | - keep an eye on
329 | - keep it simple, stupid
330 | - keep the home fires burning
331 | - keep up with the Joneses
332 | - keep your chin up
333 | - keep your fingers crossed
334 | - kick the bucket
335 | - kick up your heels
336 | - kick your feet up
337 | - kid in a candy store
338 | - kill two birds with one stone
339 | - kiss of death
340 | - knock it out of the park
341 | - knock on wood
342 | - knock your socks off
343 | - know him from Adam
344 | - know the ropes
345 | - know the score
346 | - knuckle down
347 | - knuckle sandwich
348 | - knuckle under
349 | - labor of love
350 | - ladder of success
351 | - land on your feet
352 | - lap of luxury
353 | - last but not least
354 | - last hurrah
355 | - last-ditch effort
356 | - law of the jungle
357 | - law of the land
358 | - lay down the law
359 | - leaps and bounds
360 | - let sleeping dogs lie
361 | - let the cat out of the bag
362 | - let the good times roll
363 | - let your hair down
364 | - let's talk turkey
365 | - letter perfect
366 | - lick your wounds
367 | - lies like a rug
368 | - life's a bitch
369 | - life's a grind
370 | - light at the end of the tunnel
371 | - lighter than a feather
372 | - lighter than air
373 | - like clockwork
374 | - like father like son
375 | - like taking candy from a baby
376 | - like there's no tomorrow
377 | - lion's share
378 | - live and learn
379 | - live and let live
380 | - long and short of it
381 | - long lost love
382 | - look before you leap
383 | - look down your nose
384 | - look what the cat dragged in
385 | - looking a gift horse in the mouth
386 | - looks like death warmed over
387 | - loose cannon
388 | - lose your head
389 | - lose your temper
390 | - loud as a horn
391 | - lounge lizard
392 | - loved and lost
393 | - low man on the totem pole
394 | - luck of the draw
395 | - luck of the Irish
396 | - make hay while the sun shines
397 | - make money hand over fist
398 | - make my day
399 | - make the best of a bad situation
400 | - make the best of it
401 | - make your blood boil
402 | - man of few words
403 | - man's best friend
404 | - mark my words
405 | - meaningful dialogue
406 | - missed the boat on that one
407 | - moment in the sun
408 | - moment of glory
409 | - moment of truth
410 | - money to burn
411 | - more power to you
412 | - more than one way to skin a cat
413 | - movers and shakers
414 | - moving experience
415 | - naked as a jaybird
416 | - naked truth
417 | - neat as a pin
418 | - needle in a haystack
419 | - needless to say
420 | - neither here nor there
421 | - never look back
422 | - never say never
423 | - nip and tuck
424 | - nip it in the bud
425 | - no guts, no glory
426 | - no love lost
427 | - no pain, no gain
428 | - no skin off my back
429 | - no stone unturned
430 | - no time like the present
431 | - no use crying over spilled milk
432 | - nose to the grindstone
433 | - not a hope in hell
434 | - not a minute's peace
435 | - not in my backyard
436 | - not playing with a full deck
437 | - not the end of the world
438 | - not written in stone
439 | - nothing to sneeze at
440 | - nothing ventured nothing gained
441 | - now we're cooking
442 | - off the top of my head
443 | - off the wagon
444 | - off the wall
445 | - old hat
446 | - older and wiser
447 | - older than dirt
448 | - older than Methuselah
449 | - on a roll
450 | - on cloud nine
451 | - on pins and needles
452 | - on the bandwagon
453 | - on the money
454 | - on the nose
455 | - on the rocks
456 | - on the spot
457 | - on the tip of my tongue
458 | - on the wagon
459 | - on thin ice
460 | - once bitten, twice shy
461 | - one bad apple doesn't spoil the bushel
462 | - one born every minute
463 | - one brick short
464 | - one foot in the grave
465 | - one in a million
466 | - one red cent
467 | - only game in town
468 | - open a can of worms
469 | - open and shut case
470 | - open the flood gates
471 | - opportunity doesn't knock twice
472 | - out of pocket
473 | - out of sight, out of mind
474 | - out of the frying pan into the fire
475 | - out of the woods
476 | - out on a limb
477 | - over a barrel
478 | - over the hump
479 | - pain and suffering
480 | - pain in the
481 | - panic button
482 | - par for the course
483 | - part and parcel
484 | - party pooper
485 | - pass the buck
486 | - patience is a virtue
487 | - pay through the nose
488 | - penny pincher
489 | - perfect storm
490 | - pig in a poke
491 | - pile it on
492 | - pillar of the community
493 | - pin your hopes on
494 | - pitter patter of little feet
495 | - plain as day
496 | - plain as the nose on your face
497 | - play by the rules
498 | - play your cards right
499 | - playing the field
500 | - playing with fire
501 | - pleased as punch
502 | - plenty of fish in the sea
503 | - point with pride
504 | - poor as a church mouse
505 | - pot calling the kettle black
506 | - pretty as a picture
507 | - pull a fast one
508 | - pull your punches
509 | - pulling your leg
510 | - pure as the driven snow
511 | - put it in a nutshell
512 | - put one over on you
513 | - put the cart before the horse
514 | - put the pedal to the metal
515 | - put your best foot forward
516 | - put your foot down
517 | - quick as a bunny
518 | - quick as a lick
519 | - quick as a wink
520 | - quick as lightning
521 | - quiet as a dormouse
522 | - rags to riches
523 | - raining buckets
524 | - raining cats and dogs
525 | - rank and file
526 | - rat race
527 | - reap what you sow
528 | - red as a beet
529 | - red herring
530 | - reinvent the wheel
531 | - rich and famous
532 | - rings a bell
533 | - ripe old age
534 | - ripped me off
535 | - rise and shine
536 | - road to hell is paved with good intentions
537 | - rob Peter to pay Paul
538 | - roll over in the grave
539 | - rub the wrong way
540 | - ruled the roost
541 | - running in circles
542 | - sad but true
543 | - sadder but wiser
544 | - salt of the earth
545 | - scared stiff
546 | - scared to death
547 | - sealed with a kiss
548 | - second to none
549 | - see eye to eye
550 | - seen the light
551 | - seize the day
552 | - set the record straight
553 | - set the world on fire
554 | - set your teeth on edge
555 | - sharp as a tack
556 | - shoot for the moon
557 | - shoot the breeze
558 | - shot in the dark
559 | - shoulder to the wheel
560 | - sick as a dog
561 | - sigh of relief
562 | - signed, sealed, and delivered
563 | - sink or swim
564 | - six of one, half a dozen of another
565 | - skating on thin ice
566 | - slept like a log
567 | - slinging mud
568 | - slippery as an eel
569 | - slow as molasses
570 | - smart as a whip
571 | - smooth as a baby's bottom
572 | - sneaking suspicion
573 | - snug as a bug in a rug
574 | - sow wild oats
575 | - spare the rod, spoil the child
576 | - speak of the devil
577 | - spilled the beans
578 | - spinning your wheels
579 | - spitting image of
580 | - spoke with relish
581 | - spread like wildfire
582 | - spring to life
583 | - squeaky wheel gets the grease
584 | - stands out like a sore thumb
585 | - start from scratch
586 | - stick in the mud
587 | - still waters run deep
588 | - stitch in time
589 | - stop and smell the roses
590 | - straight as an arrow
591 | - straw that broke the camel's back
592 | - strong as an ox
593 | - stubborn as a mule
594 | - stuff that dreams are made of
595 | - stuffed shirt
596 | - sweating blood
597 | - sweating bullets
598 | - take a load off
599 | - take one for the team
600 | - take the bait
601 | - take the bull by the horns
602 | - take the plunge
603 | - takes one to know one
604 | - takes two to tango
605 | - the more the merrier
606 | - the real deal
607 | - the real McCoy
608 | - the red carpet treatment
609 | - the same old story
610 | - there is no accounting for taste
611 | - thick as a brick
612 | - thick as thieves
613 | - thin as a rail
614 | - think outside of the box
615 | - third time's the charm
616 | - this day and age
617 | - this hurts me worse than it hurts you
618 | - this point in time
619 | - three sheets to the wind
620 | - through thick and thin
621 | - throw in the towel
622 | - tie one on
623 | - tighter than a drum
624 | - time and time again
625 | - time is of the essence
626 | - tip of the iceberg
627 | - tired but happy
628 | - to coin a phrase
629 | - to each his own
630 | - to make a long story short
631 | - to the best of my knowledge
632 | - toe the line
633 | - tongue in cheek
634 | - too good to be true
635 | - too hot to handle
636 | - too numerous to mention
637 | - touch with a ten foot pole
638 | - tough as nails
639 | - trial and error
640 | - trials and tribulations
641 | - tried and true
642 | - trip down memory lane
643 | - twist of fate
644 | - two cents worth
645 | - two peas in a pod
646 | - ugly as sin
647 | - under the counter
648 | - under the gun
649 | - under the same roof
650 | - under the weather
651 | - until the cows come home
652 | - unvarnished truth
653 | - up the creek
654 | - uphill battle
655 | - upper crust
656 | - upset the applecart
657 | - vain attempt
658 | - vain effort
659 | - vanquish the enemy
660 | - vested interest
661 | - waiting for the other shoe to drop
662 | - wakeup call
663 | - warm welcome
664 | - watch your p's and q's
665 | - watch your tongue
666 | - watching the clock
667 | - water under the bridge
668 | - weather the storm
669 | - weed them out
670 | - week of Sundays
671 | - went belly up
672 | - wet behind the ears
673 | - what goes around comes around
674 | - what you see is what you get
675 | - when it rains, it pours
676 | - when push comes to shove
677 | - when the cat's away
678 | - when the going gets tough, the tough get going
679 | - white as a sheet
680 | - whole ball of wax
681 | - whole hog
682 | - whole nine yards
683 | - wild goose chase
684 | - will wonders never cease?
685 | - wisdom of the ages
686 | - wise as an owl
687 | - wolf at the door
688 | - words fail me
689 | - work like a dog
690 | - world weary
691 | - worst nightmare
692 | - worth its weight in gold
693 | - wrong side of the bed
694 | - yanking your chain
695 | - yappy as a dog
696 | - years young
697 | - you are what you eat
698 | - you can run but you can't hide
699 | - you only live once
700 | - you're the boss
701 | - young and foolish
702 | - young and vibrant
703 |
--------------------------------------------------------------------------------