Skip to content

Commit

Permalink
Merge pull request #174 from dviererbe/add-optional-dependency-manage…
Browse files Browse the repository at this point in the history
…ment-mechanism

undefined
  • Loading branch information
ru-fu committed Jan 25, 2024
2 parents 585ab49 + bf41e17 commit 0d74850
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,5 +1,6 @@
/*env*/
.sphinx/venv/
.sphinx/requirements.txt
.sphinx/warnings.txt
.sphinx/.wordlist.dic
.sphinx/.doctrees/
Expand Down
3 changes: 3 additions & 0 deletions .readthedocs.yaml
Expand Up @@ -10,6 +10,9 @@ build:
os: ubuntu-22.04
tools:
python: "3.11"
jobs:
post_checkout:
- python3 build_requirements.py

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
14 changes: 0 additions & 14 deletions .sphinx/requirements.txt

This file was deleted.

4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -19,9 +19,8 @@ VENV = $(VENVDIR)/bin/activate
help: $(VENVDIR)
@. $(VENV); $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

# Explicit target avoids fall-through to the "Makefile" target.
$(SPHINXDIR)/requirements.txt:
test -f $(SPHINXDIR)/requirements.txt
python3 build_requirements.py

# If requirements are updated, venv should be rebuilt and timestamped.
$(VENVDIR): $(SPHINXDIR)/requirements.txt
Expand Down Expand Up @@ -77,6 +76,7 @@ serve: html
clean: clean-doc
@test ! -e "$(VENVDIR)" -o -d "$(VENVDIR)" -a "$(abspath $(VENVDIR))" != "$(VENVDIR)"
rm -rf $(VENVDIR)
rm -f .sphinx/requirements.txt

clean-doc:
git clean -fx "$(BUILDDIR)"
Expand Down
124 changes: 124 additions & 0 deletions build_requirements.py
@@ -0,0 +1,124 @@
import sys

sys.path.append('./')
from custom_conf import *

# The file contains helper functions and the mechanism to build the
# .sphinx/requirements.txt file that is needed to set up the virtual
# environment.

# You should not do any modifications to this file. Put your custom
# requirements into the custom_required_modules array in the custom_conf.py
# file. If you need to change this file, contribute the changes upstream.

legacyCanonicalSphinxExtensionNames = [
"youtube-links",
"related-links",
"custom-rst-roles",
"terminal-output"
]

def IsAnyCanonicalSphinxExtensionUsed():
for extension in custom_extensions:
if (extension.startswith("canonical.") or
extension in legacyCanonicalSphinxExtensionNames):
return True

return False

def IsNotFoundExtensionUsed():
return "notfound.extension" in custom_extensions

def IsSphinxTabsUsed():
for extension in custom_extensions:
if extension.startswith("sphinx_tabs."):
return True

return False

def AreRedirectsDefined():
return ("sphinx_reredirects" in custom_extensions) or (
("redirects" in globals()) and \
(redirects is not None) and \
(len(redirects) > 0))

def IsOpenGraphConfigured():
if "sphinxext.opengraph" in custom_extensions:
return True

for global_variable_name in list(globals()):
if global_variable_name.startswith("ogp_"):
return True

return False

def IsMyStParserUsed():
return ("myst_parser" in custom_extensions) or \
("custom_myst_extensions" in globals())

def DeduplicateExtensions(extensionNames: [str]):
extensionNames = dict.fromkeys(extensionNames)
resultList = []
encounteredCanonicalExtensions = []

for extensionName in extensionNames:
if extensionName in legacyCanonicalSphinxExtensionNames:
extensionName = "canonical." + extensionName

if extensionName.startswith("canonical."):
if extensionName not in encounteredCanonicalExtensions:
encounteredCanonicalExtensions.append(extensionName)
resultList.append(extensionName)
else:
resultList.append(extensionName)

return resultList

if __name__ == "__main__":
requirements = [
"furo",
"pyspelling",
"sphinx",
"sphinx-autobuild",
"sphinx-copybutton",
"sphinx-design",
"sphinxcontrib-jquery"
]

requirements.extend(custom_required_modules)

if IsAnyCanonicalSphinxExtensionUsed():
requirements.append("canonical-sphinx-extensions")

if IsNotFoundExtensionUsed():
requirements.append("sphinx-notfound-page")

if IsSphinxTabsUsed():
requirements.append("sphinx-tabs")

if AreRedirectsDefined():
requirements.append("sphinx-reredirects")

if IsOpenGraphConfigured():
requirements.append("sphinxext-opengraph")

if IsMyStParserUsed():
requirements.append("myst-parser")
requirements.append("linkify-it-py")

# removes duplicate entries
requirements = list(dict.fromkeys(requirements))
requirements.sort()

with open(".sphinx/requirements.txt", 'w') as requirements_file:
requirements_file.write(
"# DO NOT MODIFY THIS FILE DIRECTLY!\n"
"#\n"
"# This file is generated automatically.\n"
"# Add custom requirements to the custom_required_modules\n"
"# array in the custom_conf.py file and run:\n"
"# make clean && make install\n")

for requirement in requirements:
requirements_file.write(requirement)
requirements_file.write('\n')
40 changes: 23 additions & 17 deletions conf.py
Expand Up @@ -2,6 +2,7 @@

sys.path.append('./')
from custom_conf import *
from build_requirements import *

# Configuration file for the Sphinx documentation builder.
# You should not do any modifications to this file. Put your custom
Expand All @@ -17,30 +18,35 @@

extensions = [
'sphinx_design',
'sphinx_tabs.tabs',
'sphinx_reredirects',
'canonical.youtube-links',
'canonical.related-links',
'canonical.custom-rst-roles',
'canonical.terminal-output',
'sphinx_copybutton',
'sphinxext.opengraph',
'myst_parser',
'sphinxcontrib.jquery',
'notfound.extension'
]

# Only add redirects extension if any redirects are specified.
if AreRedirectsDefined():
extensions.append('sphinx_reredirects')

# Only add myst extensions if any configuration is present.
if IsMyStParserUsed():
extensions.append('myst_parser')

# Additional MyST syntax
myst_enable_extensions = [
'substitution',
'deflist',
'linkify'
]
myst_enable_extensions.extend(custom_myst_extensions)

# Only add Open Graph extension if any configuration is present.
if IsOpenGraphConfigured():
extensions.append('sphinxext.opengraph')

extensions.extend(custom_extensions)
extensions = DeduplicateExtensions(extensions)

### Configuration for extensions

# Additional MyST syntax
myst_enable_extensions = [
'substitution',
'deflist',
'linkify'
]
myst_enable_extensions.extend(custom_myst_extensions)

# Used for related links
if not 'discourse_prefix' in html_context and 'discourse' in html_context:
html_context['discourse_prefix'] = html_context['discourse'] + '/t/'
Expand Down
39 changes: 29 additions & 10 deletions custom_conf.py
Expand Up @@ -47,6 +47,8 @@
## when linking to the documentation from another website (see https://ogp.me/)
# The URL where the documentation will be hosted (leave empty if you
# don't know yet)
# NOTE: If no ogp_* variable is defined (e.g. if you remove this section) the
# sphinxext.opengraph extension will be disabled.
ogp_site_url = 'https://canonical-starter-pack.readthedocs-hosted.com/'
# The documentation website name (usually the same as the product name)
ogp_site_name = project
Expand Down Expand Up @@ -113,42 +115,59 @@

# Set up redirects (https://documatt.gitlab.io/sphinx-reredirects/usage.html)
# For example: 'explanation/old-name.html': '../how-to/prettify.html',

# NOTE: If this variable is not defined, set to None, or the dictionary is empty,
# the sphinx_reredirects extension will be disabled.
redirects = {}

############################################################
### Link checker exceptions
############################################################

# Links to ignore when checking links

linkcheck_ignore = [
'http://127.0.0.1:8000'
]

# Pages on which to ignore anchors
# (This list will be appended to linkcheck_anchors_ignore_for_url)

custom_linkcheck_anchors_ignore_for_url = [
]
custom_linkcheck_anchors_ignore_for_url = []

############################################################
### Additions to default configuration
############################################################

## The following settings are appended to the default configuration.
## Use them to extend the default functionality.
# NOTE: Remove this variable to disable the MyST parser extensions.
custom_myst_extensions = []

# Add extensions
custom_extensions = []
# Add custom Sphinx extensions as needed.
# This array contains recommended extensions that should be used.
# NOTE: The following extensions are handled automatically and do
# not need to be added here: myst_parser, sphinx_copybutton, sphinx_design,
# sphinx_reredirects, sphinxcontrib.jquery, sphinxext.opengraph
custom_extensions = [
'sphinx_tabs.tabs',
'canonical.youtube-links',
'canonical.related-links',
'canonical.custom-rst-roles',
'canonical.terminal-output',
'notfound.extension'
]

# Add MyST extensions
custom_myst_extensions = []
# Add custom required Python modules that must be added to the
# .sphinx/requirements.txt file.
# NOTE: The following modules are handled automatically and do not need to be
# added here: canonical-sphinx-extensions, furo, linkify-it-py, myst-parser,
# pyspelling, sphinx, sphinx-autobuild, sphinx-copybutton, sphinx-design,
# sphinx-notfound-page, sphinx-reredirects, sphinx-tabs, sphinxcontrib-jquery,
# sphinxext-opengraph
custom_required_modules = []

# Add files or directories that should be excluded from processing.
custom_excludes = [
'doc-cheat-sheet*',
]
]

# Add CSS files (located in .sphinx/_static/)
custom_html_css_files = []
Expand Down
1 change: 1 addition & 0 deletions init.sh
Expand Up @@ -16,6 +16,7 @@ rm -rf "$temp_directory/.git"
echo "Updating working directory in workflow files..."
sed -i "s|working-directory:\s*'\.'|working-directory: '$install_directory'|g" "$temp_directory/.github/workflows"/*
echo "Updating .readthedocs.yaml configuration..."
sed -i "s|-\s\s*python3\s\s*build_requirements\.py|- cd '$install_directory' \&\& python3 build_requirements.py|g" "$temp_directory/.readthedocs.yaml"
sed -i "s|configuration:\s*conf\.py|configuration: $install_directory/conf.py|g" "$temp_directory/.readthedocs.yaml"
sed -i "s|requirements:\s*\.sphinx/requirements\.txt|requirements: $install_directory/.sphinx/requirements.txt|g" "$temp_directory/.readthedocs.yaml"

Expand Down
19 changes: 14 additions & 5 deletions readme.rst
Expand Up @@ -117,6 +117,7 @@ To add documentation to an existing code repository:
#. create a symbolic link to the ``docs/.wokeignore`` file from the root directory of the code repository
#. in file ``docs/.readthedocs.yaml`` set the following:

* ``post_checkout: cd docs && python3 build_requirements.py``
* ``configuration: docs/conf.py``
* ``requirements: docs/.sphinx/requirements.txt``

Expand Down Expand Up @@ -203,13 +204,16 @@ They are pre-configured as needed, but you can customise their configuration in
The following extensions are always included:

- |sphinx-design|_
- |sphinx_copybutton|_
- |sphinxcontrib.jquery|_

The following extensions will automatically be included based on the configuration in the ``custom_conf.py`` file:

- |sphinx_tabs.tabs|_
- |sphinx_reredirects|_
- |lxd-sphinx-extensions|_ (``youtube-links``, ``related-links``, ``custom-rst-roles``, and ``terminal-output``)
- |sphinx_copybutton|_
- |sphinxext.opengraph|_
- |lxd-sphinx-extensions|_ (``youtube-links``, ``related-links``, ``custom-rst-roles``, and ``terminal-output``)
- |myst_parser|_
- |sphinxcontrib.jquery|_
- |notfound.extension|_

You can add further extensions in the ``custom_extensions`` variable in ``custom_conf.py``.
Expand Down Expand Up @@ -253,8 +257,13 @@ To install the prerequisites:
make install
This will create a virtual environment (``.sphinx/venv``) and install
dependency software (``.sphinx/requirements.txt``) within it.
This will create the required software list (``.sphinx/requirements.txt``)
which is used to create a virtual environment (``.sphinx/venv``) and install
dependency software within it.

You can add further Python modules to the required software list
(``.sphinx/requirements.txt```) in the ``custom_required_modules`` variable
in the ``custom_conf.py`` file.

**Note**:
By default, the starter pack uses the latest compatible version of all tools and does not pin its requirements.
Expand Down

0 comments on commit 0d74850

Please sign in to comment.