update package and add myst extensions , update convert2md script

This commit is contained in:
hoangvv 2025-02-28 00:14:45 +07:00
parent 637a4dd9fd
commit fb57c796ee
3 changed files with 447 additions and 228 deletions

524
conf.py
View File

@ -13,33 +13,33 @@ from sphinx.util import logging
_logger = logging.getLogger(__name__)
#=== General configuration ===#
# === General configuration ===#
# General information about the project.
project = 'Odoo'
copyright = 'Odoo S.A.'
project = "Odoo"
copyright = "Odoo S.A."
# `version` is the version info for the project being documented, acts as replacement for |version|,
# also used in various other places throughout the built documents.
# `release` is the full version, including alpha/beta/rc tags. Acts as replacement for |release|.
version = release = '18.0'
version = release = "18.0"
# `current_branch` is the technical name of the current branch.
# E.g., saas-15.4 -> saas-15.4; 12.0 -> 12.0, master -> master (*).
current_branch = version
# `current_version` is the Odoo version linked to the current branch.
# E.g., saas-15.4 -> 15.4; 12.0 -> 12; master -> master (*).
current_version = current_branch.replace('saas-', '').replace('.0', '')
current_version = current_branch.replace("saas-", "").replace(".0", "")
# `current_major_branch` is the technical name of the major branch before the current branch.
# E.g., saas-15.4 -> 15.0; 12.0 -> 12.0; master -> master (*).
current_major_branch = re.sub(r'\.\d', '.0', current_branch.replace('saas-', ''))
current_major_branch = re.sub(r"\.\d", ".0", current_branch.replace("saas-", ""))
# `current_major_version` is the Odoo version linked to the current major branch.
# E.g., saas-15.4 -> 15; 12.0 -> 12; master -> master (*).
current_major_version = current_major_branch.replace('.0', '')
current_major_version = current_major_branch.replace(".0", "")
# (*): We don't care for master.
# The minimal Sphinx version required to build the documentation.
needs_sphinx = '3.0.0'
needs_sphinx = "3.0.0"
# The default language in which the documentation is written. It is set to `None` because Sphinx
# considers that no language means 'en'.
@ -47,26 +47,28 @@ language = None
# The suffix of source filenames.
source_suffix = {
'.rst': 'restructuredtext',
'.md': 'markdown',
".rst": "restructuredtext",
".md": "markdown",
}
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# List of patterns, relative to source directory, that match files and directories to ignore when
# looking for source files.
exclude_patterns = [
'locale',
'README.*',
'bin', 'include', 'lib',
'odoo',
"locale",
"README.*",
"bin",
"include",
"lib",
"odoo",
]
# The RST text role to use when the role is not specified. E.g.: `example`.
# We use 'literal' as default role for markdown compatibility: `foo` behaves like ``foo``.
# See https://docutils.sourceforge.io/docs/ref/rst/roles.html#standard-roles for other roles.
default_role = 'literal'
default_role = "literal"
html_copy_source = False
# Whether scaled down images should be be wrapped in a `<a/>` tag linking to the image file or not.
@ -75,28 +77,28 @@ html_scaled_image_link = False
# If true, '()' will be appended to :func: etc. cross-reference text
add_function_parentheses = True
#=== Extensions configuration ===#
# === Extensions configuration ===#
source_read_replace_vals = {
'BRANCH': current_branch,
'CURRENT_BRANCH': current_branch,
'CURRENT_VERSION': current_version,
'CURRENT_MAJOR_BRANCH': current_major_branch,
'CURRENT_MAJOR_VERSION': current_major_version,
'GITHUB_PATH': f'https://github.com/odoo/odoo/blob/{version}',
'GITHUB_ENT_PATH': f'https://github.com/odoo/enterprise/blob/{version}',
'OWL_PATH': f'https://github.com/odoo/owl/blob/master',
"BRANCH": current_branch,
"CURRENT_BRANCH": current_branch,
"CURRENT_VERSION": current_version,
"CURRENT_MAJOR_BRANCH": current_major_branch,
"CURRENT_MAJOR_VERSION": current_major_version,
"GITHUB_PATH": f"https://github.com/odoo/odoo/blob/{version}",
"GITHUB_ENT_PATH": f"https://github.com/odoo/enterprise/blob/{version}",
"OWL_PATH": f"https://github.com/odoo/owl/blob/master",
}
# Add extensions directory to PYTHONPATH
extension_dir = Path('extensions')
extension_dir = Path("extensions")
sys.path.insert(0, str(extension_dir.absolute()))
# Search for the directory of odoo sources to know whether autodoc should be used on the dev doc
# odoo_sources_candidate_dirs = (Path('Odoo18'), Path('../Odoo18'))
odoo_sources_candidate_dirs = (Path('odoo'), Path('../odoo'))
odoo_sources_candidate_dirs = (Path("Odoo18"), Path("../Odoo18"))
# odoo_sources_candidate_dirs = (Path('odoo'), Path('../odoo'))
odoo_sources_dirs = [
d for d in odoo_sources_candidate_dirs if d.is_dir() and (d / 'odoo-bin').exists()
d for d in odoo_sources_candidate_dirs if d.is_dir() and (d / "odoo-bin").exists()
]
odoo_dir_in_path = False
@ -107,20 +109,30 @@ if not odoo_sources_dirs:
"The 'Developer' documentation will be built but autodoc directives will be skipped.\n"
"In order to fully build the 'Developer' documentation, clone the repository with "
"`git clone https://github.com/odoo/odoo` or create a symbolic link.",
{'dir_list': '\n'.join([f'\t- {d.resolve()}' for d in odoo_sources_candidate_dirs])},
{
"dir_list": "\n".join(
[f"\t- {d.resolve()}" for d in odoo_sources_candidate_dirs]
)
},
)
else:
if (3, 6) < sys.version_info < (3, 7):
# Running odoo needs python 3.7 min but monkey patch version_info to be compatible with 3.6.
sys.version_info = (3, 7, 0)
odoo_dir = odoo_sources_dirs[0].resolve()
source_read_replace_vals['ODOO_RELPATH'] = '/../' + str(odoo_sources_dirs[0])
source_read_replace_vals["ODOO_RELPATH"] = "/../" + str(odoo_sources_dirs[0])
sys.path.insert(0, str(odoo_dir))
import odoo.addons
odoo.addons.__path__.append(str(odoo_dir) + '/addons')
from odoo import release as odoo_release # Don't collide with Sphinx's 'release' config option
odoo_version = '.'.join(str(s) for s in odoo_release.version_info[:2]).replace('~', '-') # Change saas~XX.Y to saas-XX.Y
odoo_version = 'master' if 'alpha' in odoo_release.version else odoo_version
odoo.addons.__path__.append(str(odoo_dir) + "/addons")
from odoo import (
release as odoo_release,
) # Don't collide with Sphinx's 'release' config option
odoo_version = ".".join(str(s) for s in odoo_release.version_info[:2]).replace(
"~", "-"
) # Change saas~XX.Y to saas-XX.Y
odoo_version = "master" if "alpha" in odoo_release.version else odoo_version
if release != odoo_version:
_logger.warning(
"Found Odoo sources in %(directory)s but with version '%(odoo_version)s' incompatible "
@ -128,17 +140,23 @@ else:
"The 'Developer' documentation will be built but autodoc directives will be skipped.\n"
"In order to fully build the 'Developer' documentation, checkout the matching branch"
" with `cd odoo && git checkout %(doc_version)s`.",
{'directory': odoo_dir, 'odoo_version': odoo_version, 'doc_version': version},
{
"directory": odoo_dir,
"odoo_version": odoo_version,
"doc_version": version,
},
)
else:
_logger.info(
"Found Odoo sources in %(directory)s matching documentation version '%(version)s'.",
{'directory': odoo_dir, 'version': release},
{"directory": odoo_dir, "version": release},
)
odoo_dir_in_path = True
if odoo_dir_in_path:
upgrade_util_dir = next(filter(Path.exists, [Path('upgrade-util'), Path('../upgrade-util')]), None)
upgrade_util_dir = next(
filter(Path.exists, [Path("upgrade-util"), Path("../upgrade-util")]), None
)
if not upgrade_util_dir:
_logger.warning(
"Could not find Upgrade Utils sources directory in `upgrade_util`.\n"
@ -150,153 +168,165 @@ if odoo_dir_in_path:
else:
_logger.info(
"Found Upgrade Util sources in %(directory)s",
{'directory': upgrade_util_dir.resolve()},
{"directory": upgrade_util_dir.resolve()},
)
from odoo import upgrade
upgrade.__path__.append(str((upgrade_util_dir / 'src').resolve()))
upgrade.__path__.append(str((upgrade_util_dir / "src").resolve()))
# Mapping between odoo models related to master data and the declaration of the
# data. This is used to point users to available xml_ids when giving values for
# a field with the autodoc_field extension.
model_references = {
'account.account.type': 'addons/account/data/data_account_type.xml',
'res.country': 'odoo/addons/base/data/res_country_data.xml',
'res.currency': 'odoo/addons/base/data/res_currency_data.xml',
"account.account.type": "addons/account/data/data_account_type.xml",
"res.country": "odoo/addons/base/data/res_country_data.xml",
"res.currency": "odoo/addons/base/data/res_currency_data.xml",
}
# The Sphinx extensions to use, as module names.
# They can be extensions coming with Sphinx (named 'sphinx.ext.*') or custom ones.
extensions = [
# Link sources in other projects (used to build the reference doc)
'sphinx.ext.intersphinx',
"sphinx.ext.intersphinx",
# Support the specialized to-do directives
'sphinx.ext.todo',
"sphinx.ext.todo",
# Custom Odoo theme
'odoo_theme',
"odoo_theme",
# Youtube and Vimeo videos integration (youtube, vimeo directives)
'embedded_video',
'custom_admonitions',
"embedded_video",
"custom_admonitions",
# Redirection generator
'redirects',
"redirects",
# Content tabs
'sphinx_tabs.tabs',
"sphinx_tabs.tabs",
# Cards
'cards',
"cards",
# Spoilers
'spoilers',
"spoilers",
# Strange html domain logic used in memento pages
'html_domain',
'myst_parser',
"html_domain",
"myst_parser",
]
myst_enable_extensions = [
"amsmath",
"colon_fence",
"deflist",
"dollarmath",
"fieldlist",
"html_admonition",
"html_image",
"linkify",
"replacements",
"smartquotes",
"strikethrough",
"substitution",
"tasklist",
]
if odoo_dir_in_path:
# GitHub links generation
extensions += [
'sphinx.ext.linkcode',
'github_link',
"sphinx.ext.linkcode",
"github_link",
# Parse Python docstrings (autodoc, automodule, autoattribute directives)
'sphinx.ext.autodoc',
'autodoc_field',
"sphinx.ext.autodoc",
"autodoc_field",
]
else:
extensions += [
'autodoc_placeholder',
"autodoc_placeholder",
]
extensions.append('sphinx.ext.graphviz' if shutil.which('dot') else 'graphviz_placeholder')
extensions.append(
"sphinx.ext.graphviz" if shutil.which("dot") else "graphviz_placeholder"
)
todo_include_todos = False
intersphinx_mapping = {
'pillow': ('https://pillow.readthedocs.io/en/stable/', None),
'python': ('https://docs.python.org/3/', None),
'werkzeug': ('https://werkzeug.palletsprojects.com/en/2.3.x/', None),
"pillow": ("https://pillow.readthedocs.io/en/stable/", None),
"python": ("https://docs.python.org/3/", None),
"werkzeug": ("https://werkzeug.palletsprojects.com/en/2.3.x/", None),
}
github_user = 'odoo'
github_project = 'documentation'
github_user = "odoo"
github_project = "documentation"
locale_dirs = ['../locale/']
templates_path = ['../extensions']
locale_dirs = ["../locale/"]
templates_path = ["../extensions"]
# custom docname_to_domain to divide the translations of applications in subdirectories
sphinx.transforms.i18n.docname_to_domain = (
sphinx.util.i18n.docname_to_domain
) = lambda docname, compact: docname.split('/')[1 if docname.startswith('applications/') else 0]
sphinx.transforms.i18n.docname_to_domain = sphinx.util.i18n.docname_to_domain = (
lambda docname, compact: docname.split("/")[
1 if docname.startswith("applications/") else 0
]
)
# The version names that should be shown in the version switcher, if the config option `versions`
# is populated. If a version is passed to `versions` but is not listed here, it will not be shown.
versions_names = {
'master': "Master",
'saas-18.1': "Odoo Online",
'18.0': "18.0",
'saas-17.4': "Odoo Online",
'saas-17.2': "Odoo Online",
'17.0': "Odoo 17",
'16.0': "Odoo 16",
'15.0': "Odoo 15",
"master": "Master",
"saas-18.1": "Odoo Online",
"18.0": "18.0",
"saas-17.4": "Odoo Online",
"saas-17.2": "Odoo Online",
"17.0": "Odoo 17",
"16.0": "Odoo 16",
"15.0": "Odoo 15",
}
# The language names that should be shown in the language switcher, if the config option `languages`
# is populated. If a language is passed to `languages` but is not listed here, it will not be shown.
languages_names = {
'de': 'DE',
'en': 'EN',
'es': 'ES',
'es_419': 'ES (LATAM)',
'fr': 'FR',
'id': 'ID',
'it': 'IT',
'ja': 'JA',
'ko': 'KR',
'nl': 'NL',
'pt_BR': 'PT',
'ro': 'RO',
'sv': 'SV',
'th': 'TH',
'uk': 'UA',
'vi': 'VI',
'zh_CN': 'ZH (CN)',
'zh_TW': 'ZH (TW)'
"de": "DE",
"en": "EN",
"es": "ES",
"es_419": "ES (LATAM)",
"fr": "FR",
"id": "ID",
"it": "IT",
"ja": "JA",
"ko": "KR",
"nl": "NL",
"pt_BR": "PT",
"ro": "RO",
"sv": "SV",
"th": "TH",
"uk": "UA",
"vi": "VI",
"zh_CN": "ZH (CN)",
"zh_TW": "ZH (TW)",
}
# The directory in which files holding redirect rules used by the 'redirects' extension are listed.
redirects_dir = 'redirects/'
redirects_dir = "redirects/"
sphinx_tabs_disable_tab_closing = True
sphinx_tabs_disable_css_loading = True
# Autodoc ordering
autodoc_member_order = 'bysource'
autodoc_member_order = "bysource"
#=== Options for HTML output ===#
# === Options for HTML output ===#
html_theme = 'odoo_theme'
html_theme = "odoo_theme"
# The name of the Pygments (syntax highlighting) style to use.
# See extensions/odoo_theme/pygments_override.py
pygments_style = 'odoo'
pygments_style = "odoo"
# The paths that contain custom themes, relative to this directory.
html_theme_path = ['extensions']
html_theme_path = ["extensions"]
# The name of an image file (within the static path) to use as favicon of the docs.
# This file should be a Windows icon file (.ico) being 16x16 or 32x32 pixels large.
html_favicon = os.path.join(html_theme_path[0], html_theme, 'static', 'img', 'favicon.ico')
html_favicon = os.path.join(
html_theme_path[0], html_theme, "static", "img", "favicon.ico"
)
# The paths that contain custom static files, relative to this directory.
# They are copied after the builtin static files, so a file named "default.css" will overwrite the
# builtin "default.css".
html_static_path = ['static']
html_static_path = ["static"]
html_permalinks = True
# Additional JS & CSS files that can be imported with the 'custom-js' and 'custom-css' metadata.
@ -306,72 +336,131 @@ html_css_files = []
# PHP lexer option to not require <?php
highlight_options = {
'php': {'startinline': True},
"php": {"startinline": True},
}
#=== Options for LaTeX output ===#
# === Options for LaTeX output ===#
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
'papersize': 'a4paper',
"papersize": "a4paper",
# Additional stuff for the LaTeX preamble.
'preamble': r'\usepackage{odoo}',
'tableofcontents': '', # no TOC
"preamble": r"\usepackage{odoo}",
"tableofcontents": "", # no TOC
# Output manually in latex docs
'releasename': release,
"releasename": release,
}
latex_additional_files = ['static/latex/odoo.sty']
latex_additional_files = ["static/latex/odoo.sty"]
# Grouping the document tree into LaTeX files. List of tuples:
# (source start file, target name, title, author, documentclass [howto, manual, or own class]).
latex_documents = [
('legal/terms/enterprise_tex', 'odoo_enterprise_agreement.tex',
'Odoo Enterprise Subscription Agreement', '', 'howto'),
('legal/terms/partnership_tex',
'odoo_partnership_agreement.tex', 'Odoo Partnership Agreement', '', 'howto'),
('legal/terms/terms_of_sale',
'terms_of_sale.tex', 'Odoo Terms of Sale', '', 'howto'),
('legal/terms/i18n/enterprise_tex_fr', 'odoo_enterprise_agreement_fr.tex',
'Odoo Enterprise Subscription Agreement (FR)', '', 'howto'),
('legal/terms/i18n/partnership_tex_fr',
'odoo_partnership_agreement_fr.tex', 'Odoo Partnership Agreement (FR)', '', 'howto'),
('legal/terms/i18n/terms_of_sale_fr', 'terms_of_sale_fr.tex',
'Conditions Générales de Vente Odoo', '', 'howto'),
('legal/terms/i18n/enterprise_tex_nl', 'odoo_enterprise_agreement_nl.tex',
'Odoo Enterprise Subscription Agreement (NL)', '', 'howto'),
('legal/terms/i18n/enterprise_tex_de', 'odoo_enterprise_agreement_de.tex',
'Odoo Enterprise Subscription Agreement (DE)', '', 'howto'),
('legal/terms/i18n/terms_of_sale_de', 'terms_of_sale_de.tex',
'Allgemeine Verkaufsbedingungen Odoo', '', 'howto'),
('legal/terms/i18n/enterprise_tex_es', 'odoo_enterprise_agreement_es.tex',
'Odoo Enterprise Subscription Agreement (ES)', '', 'howto'),
('legal/terms/i18n/partnership_tex_es',
'odoo_partnership_agreement_es.tex', 'Odoo Partnership Agreement (ES)', '', 'howto'),
('legal/terms/i18n/terms_of_sale_es', 'terms_of_sale_es.tex',
'Términos Generales de Venta Odoo', '', 'howto'),
('legal/terms/i18n/enterprise_tex_pt_BR', 'odoo_enterprise_agreement_pt_BR.tex',
'Odoo Enterprise Subscription Agreement (PT)', '', 'howto'),
(
"legal/terms/enterprise_tex",
"odoo_enterprise_agreement.tex",
"Odoo Enterprise Subscription Agreement",
"",
"howto",
),
(
"legal/terms/partnership_tex",
"odoo_partnership_agreement.tex",
"Odoo Partnership Agreement",
"",
"howto",
),
(
"legal/terms/terms_of_sale",
"terms_of_sale.tex",
"Odoo Terms of Sale",
"",
"howto",
),
(
"legal/terms/i18n/enterprise_tex_fr",
"odoo_enterprise_agreement_fr.tex",
"Odoo Enterprise Subscription Agreement (FR)",
"",
"howto",
),
(
"legal/terms/i18n/partnership_tex_fr",
"odoo_partnership_agreement_fr.tex",
"Odoo Partnership Agreement (FR)",
"",
"howto",
),
(
"legal/terms/i18n/terms_of_sale_fr",
"terms_of_sale_fr.tex",
"Conditions Générales de Vente Odoo",
"",
"howto",
),
(
"legal/terms/i18n/enterprise_tex_nl",
"odoo_enterprise_agreement_nl.tex",
"Odoo Enterprise Subscription Agreement (NL)",
"",
"howto",
),
(
"legal/terms/i18n/enterprise_tex_de",
"odoo_enterprise_agreement_de.tex",
"Odoo Enterprise Subscription Agreement (DE)",
"",
"howto",
),
(
"legal/terms/i18n/terms_of_sale_de",
"terms_of_sale_de.tex",
"Allgemeine Verkaufsbedingungen Odoo",
"",
"howto",
),
(
"legal/terms/i18n/enterprise_tex_es",
"odoo_enterprise_agreement_es.tex",
"Odoo Enterprise Subscription Agreement (ES)",
"",
"howto",
),
(
"legal/terms/i18n/partnership_tex_es",
"odoo_partnership_agreement_es.tex",
"Odoo Partnership Agreement (ES)",
"",
"howto",
),
(
"legal/terms/i18n/terms_of_sale_es",
"terms_of_sale_es.tex",
"Términos Generales de Venta Odoo",
"",
"howto",
),
(
"legal/terms/i18n/enterprise_tex_pt_BR",
"odoo_enterprise_agreement_pt_BR.tex",
"Odoo Enterprise Subscription Agreement (PT)",
"",
"howto",
),
]
# List of languages that have legal translations (excluding EN). The keys must be in
# `languages_names`. These translations will have a link to their versions of the legal
# contracts, instead of the default EN one. The main legal documents are not part of the
# translations since they have legal meaning.
legal_translations = ['de', 'es', 'fr', 'nl', 'pt_BR']
legal_translations = ["de", "es", "fr", "nl", "pt_BR"]
# The name of an image file (relative to this directory) to place at the top of the title page.
latex_logo = 'static/img/odoo_logo.png'
latex_logo = "static/img/odoo_logo.png"
# If true, show URL addresses after external links.
latex_show_urls = 'True'
latex_show_urls = "True"
# https://github.com/sphinx-doc/sphinx/issues/4054#issuecomment-329097229
def source_read_replace(app, docname, source):
@ -389,36 +478,42 @@ def source_read_replace(app, docname, source):
result = result.replace(f"{{{key}}}", app.config.source_read_replace_vals[key])
source[0] = result
def setup(app):
# Generate all alternate URLs for each document
app.add_config_value('project_root', None, 'env')
app.add_config_value('canonical_version', None, 'env')
app.add_config_value('versions', None, 'env')
app.add_config_value('languages', None, 'env')
app.add_config_value('is_remote_build', None, 'env') # Whether the build is remotely deployed
app.add_config_value('source_read_replace_vals', {}, 'env')
app.connect('source-read', source_read_replace)
app.add_config_value("project_root", None, "env")
app.add_config_value("canonical_version", None, "env")
app.add_config_value("versions", None, "env")
app.add_config_value("languages", None, "env")
app.add_config_value(
"is_remote_build", None, "env"
) # Whether the build is remotely deployed
app.add_config_value("source_read_replace_vals", {}, "env")
app.connect("source-read", source_read_replace)
# TODO uncomment after moving to >= v7.2.5 to also substitute placeholders in included files.
# See https://github.com/sphinx-doc/sphinx/commit/ff1831
# app.connect('include-read', source_read_replace)
app.add_lexer('json', JsonLexer)
app.add_lexer('xml', XmlLexer)
app.add_lexer("json", JsonLexer)
app.add_lexer("xml", XmlLexer)
app.connect('html-page-context', _generate_alternate_urls)
app.connect("html-page-context", _generate_alternate_urls)
# Add a `condition` option on directives to ignore them based on config values
app.add_config_value('odoo_dir_in_path', None, 'env')
app.add_config_value("odoo_dir_in_path", None, "env")
def context_eval(expr):
return eval(expr, {confval.name: confval.value for confval in app.config})
def patch(to_patch):
to_patch.option_spec['condition'] = context_eval
to_patch.option_spec["condition"] = context_eval
original_run = to_patch.run
def new_run(self):
if not self.options.get('condition', True):
if not self.options.get("condition", True):
return []
return original_run(self)
to_patch.run = new_run
for to_patch in (
@ -429,7 +524,7 @@ def setup(app):
def _generate_alternate_urls(app, pagename, templatename, context, doctree):
""" Add keys of required alternate URLs for the current document in the rendering context.
"""Add keys of required alternate URLs for the current document in the rendering context.
Alternate URLS are required for:
- The canonical link tag
@ -438,7 +533,7 @@ def _generate_alternate_urls(app, pagename, templatename, context, doctree):
"""
def _canonicalize():
""" Add the canonical URL for the current document in the rendering context.
"""Add the canonical URL for the current document in the rendering context.
The canonical version is the last released version of the documentation.
For a given language, the canonical root of a page is in the same language so that web
@ -450,57 +545,74 @@ def _generate_alternate_urls(app, pagename, templatename, context, doctree):
"""
# If the canonical version is not set, assume that the project has a single version
_canonical_version = app.config.canonical_version or app.config.version
_canonical_lang = 'en' # Always 'en'. Don't take the value of the config option.
context['canonical'] = _build_url(_version=_canonical_version, _lang=_canonical_lang)
_canonical_lang = (
"en" # Always 'en'. Don't take the value of the config option.
)
context["canonical"] = _build_url(
_version=_canonical_version, _lang=_canonical_lang
)
def _versionize():
""" Add the pairs of (version, url) for the current document in the rendering context.
"""Add the pairs of (version, url) for the current document in the rendering context.
The entry 'version' is added by Sphinx in the rendering context.
"""
context['version_display_name'] = versions_names[version]
context["version_display_name"] = versions_names[version]
# If the list of versions is not set, assume the project has no alternate version
_provided_versions = app.config.versions and app.config.versions.split(',') or []
_provided_versions = (
app.config.versions and app.config.versions.split(",") or []
)
# Map alternate versions to their display names and URLs.
context['alternate_versions'] = []
context["alternate_versions"] = []
for _alternate_version, _display_name in versions_names.items():
if _alternate_version in _provided_versions and _alternate_version != version:
context['alternate_versions'].append(
if (
_alternate_version in _provided_versions
and _alternate_version != version
):
context["alternate_versions"].append(
(_display_name, _build_url(_alternate_version))
)
def _localize():
""" Add the pairs of (lang, code, url) for the current document in the rendering context.
"""Add the pairs of (lang, code, url) for the current document in the rendering context.
E.g.: ('French', 'fr', 'https://.../fr_BE/...')
The entry 'language' is added by Sphinx in the rendering context.
"""
_current_lang = app.config.language or 'en'
_current_lang = app.config.language or "en"
# Replace the context value by its upper-cased value ("FR" instead of "fr")
context['language'] = languages_names.get(_current_lang, _current_lang.upper())
context['language_code'] = _current_lang
context["language"] = languages_names.get(_current_lang, _current_lang.upper())
context["language_code"] = _current_lang
# If the list of languages is not set, assume that the project has no alternate language
_provided_languages = app.config.languages and app.config.languages.split(',') or []
_provided_languages = (
app.config.languages and app.config.languages.split(",") or []
)
# Map alternate languages to their display names and URLs.
context['alternate_languages'] = []
context["alternate_languages"] = []
for _alternate_lang, _display_name in languages_names.items():
if _alternate_lang in _provided_languages and _alternate_lang != _current_lang:
context['alternate_languages'].append(
if (
_alternate_lang in _provided_languages
and _alternate_lang != _current_lang
):
context["alternate_languages"].append(
(
_display_name,
_alternate_lang.split('_')[0] if _alternate_lang != 'en' else 'x-default',
(
_alternate_lang.split("_")[0]
if _alternate_lang != "en"
else "x-default"
),
_build_url(_lang=_alternate_lang),
)
)
# Dynamic generation of localized legal doc links
context['legal_translations'] = legal_translations
context["legal_translations"] = legal_translations
def _build_url(_version=None, _lang=None):
# print(f"###################################{app.config.is_remote_build}")
@ -510,34 +622,40 @@ def _generate_alternate_urls(app, pagename, templatename, context, doctree):
_root = app.config.project_root
else:
# Project root like .../documentation/_build/html/14.0/fr
_root = re.sub(rf'(/{app.config.version})?(/{app.config.language})?$', '', app.outdir)
_root = re.sub(
rf"(/{app.config.version})?(/{app.config.language})?$", "", app.outdir
)
# If the canonical version is not set, assume that the project has a single version
_canonical_version = app.config.canonical_version or app.config.version
_version = _version or app.config.version
_lang = _lang or app.config.language or 'en'
_canonical_page = f'{pagename}.html'
_lang = _lang or app.config.language or "en"
_canonical_page = f"{pagename}.html"
# legal translations have different URLs schemes as they are not managed on transifex
# e.g. FR translation of /terms/enterprise => /fr/terms/enterprise_fr
if pagename.startswith('legal/terms/'):
if pagename.startswith("legal/terms/"):
if _lang in legal_translations and not pagename.endswith(f"_{_lang}"):
# remove language code for current translation, set target one
_page = re.sub("_[a-z]{2}$", "", pagename)
if 'terms/i18n' not in _page:
if "terms/i18n" not in _page:
_page = _page.replace("/terms/", "/terms/i18n/")
_canonical_page = f'{_page}_{_lang}.html'
elif _lang == 'en' and pagename.endswith(tuple(f"_{l}" for l in legal_translations)):
_canonical_page = f"{_page}_{_lang}.html"
elif _lang == "en" and pagename.endswith(
tuple(f"_{l}" for l in legal_translations)
):
# remove language code for current translation, link to original EN one
_page = re.sub("_[a-z]{2}$", "", pagename)
_canonical_page = f'{_page.replace("/i18n/", "/")}.html'
if app.config.is_remote_build:
_canonical_page = _canonical_page.replace('index.html', '')
_canonical_page = _canonical_page.replace("index.html", "")
return f'{_root}' \
f'{f"/{_version}" if app.config.versions else ""}' \
f'{f"/{_lang}" if _lang != "en" else ""}' \
f'/{_canonical_page}'
return (
f"{_root}"
f'{f"/{_version}" if app.config.versions else ""}'
f'{f"/{_lang}" if _lang != "en" else ""}'
f"/{_canonical_page}"
)
_canonicalize()
_versionize()

View File

@ -1,19 +1,15 @@
#!/bin/bash
# Enable stricter error handling
set -euo pipefail
# Check arguments
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <source_folder> <target_folder>" >&2
exit 1
fi
readonly SOURCE_DIR="$1"
readonly TARGET_DIR="$2"
readonly TEMP_ERROR=$(mktemp)
readonly LOG_FILE="/tmp/convert2md_$$.log"
# Trap to clean up temp files on exit
trap 'rm -f "$TEMP_ERROR" "$LOG_FILE"' EXIT
@ -23,12 +19,10 @@ validate_inputs() {
echo "Error: Source directory '$SOURCE_DIR' does not exist." >&2
exit 1
}
mkdir -p "$TARGET_DIR" || {
echo "Error: Could not create target directory '$TARGET_DIR'." >&2
exit 1
}
command -v rst2myst >/dev/null 2>&1 || {
echo "Error: rst2myst is not installed. Install with 'pip install rst-to-myst'." >&2
exit 1
@ -42,18 +36,18 @@ process_rst_file() {
local md_file_name="${relative_path%.rst}.md"
local target_file="$TARGET_DIR/$md_file_name"
local target_dir=$(dirname "$target_file")
mkdir -p "$target_dir" || {
echo "Error: Could not create directory '$target_dir' for '$rst_file'" >&2
return 1
}
if rst2myst stream "$rst_file" > "$target_file" 2>>"$TEMP_ERROR"; then
echo "Converted: $rst_file -> $target_file" | tee -a "$LOG_FILE"
echo "converted" >> "$TEMP_ERROR"
return 0
else
echo "Failed to convert: $rst_file" >&2
cat "$TEMP_ERROR" >&2
echo "failed" >> "$TEMP_ERROR"
return 1
fi
}
@ -64,7 +58,6 @@ copy_non_rst_file() {
local relative_path="${file#$SOURCE_DIR/}"
local target_file="$TARGET_DIR/$relative_path"
local target_dir=$(dirname "$target_file")
mkdir -p "$target_dir" && cp -p "$file" "$target_file" 2>/dev/null && {
echo "Copied: $file -> $target_file" | tee -a "$LOG_FILE"
return 0
@ -78,9 +71,11 @@ main() {
validate_inputs
echo "Starting conversion process..." | tee "$LOG_FILE"
local rst_processed=0 rst_failed=0 copied=0
local parallel_jobs=$(( $(nproc) / 2 )) # Use half the CPU cores to avoid overload
# Count total RST files
readonly TOTAL_RST_FILES=$(find "$SOURCE_DIR" -type f -name "*.rst" | wc -l)
echo "Found $TOTAL_RST_FILES RST files to process" | tee -a "$LOG_FILE"
local parallel_jobs=$(( $(nproc) / 2 )) # Use half the CPU cores to avoid overload
# Export functions for xargs
export -f process_rst_file copy_non_rst_file
export SOURCE_DIR TARGET_DIR TEMP_ERROR LOG_FILE
@ -96,17 +91,23 @@ main() {
}
# Count results from log
rst_processed=$(grep -c "^Converted:" "$LOG_FILE" || true)
rst_failed=$(grep -c "^Failed to convert:" "$LOG_FILE" || true)
copied=$(grep -c "^Copied:" "$LOG_FILE" || true)
readonly SUCCESSFUL_CONVERSIONS=$(grep -c "^Converted:" "$LOG_FILE")
readonly FAILED_CONVERSIONS=$(grep -c "^Failed to convert:" "$LOG_FILE")
# Summary
echo "Conversion and copy process complete." | tee -a "$LOG_FILE"
echo "RST files processed successfully: $rst_processed" | tee -a "$LOG_FILE"
echo "RST files failed: $rst_failed" | tee -a "$LOG_FILE"
echo "Non-RST files copied: $copied" | tee -a "$LOG_FILE"
echo "Total RST files found: $TOTAL_RST_FILES" | tee -a "$LOG_FILE"
echo "RST files successfully converted: $SUCCESSFUL_CONVERSIONS" | tee -a "$LOG_FILE"
echo "RST files failed to convert: $FAILED_CONVERSIONS" | tee -a "$LOG_FILE"
echo "Non-RST files copied: $(grep -c "^Copied:" "$LOG_FILE")" | tee -a "$LOG_FILE"
[[ $rst_failed -gt 0 ]] && {
# Check if all files were processed
if [[ $((TOTAL_RST_FILES)) != $((SUCCESSFUL_CONVERSIONS + FAILED_CONVERSIONS)) ]]; then
echo "Warning: Some RST files might not have been processed!" >&2
exit 1
fi
[[ $FAILED_CONVERSIONS -gt 0 ]] && {
echo "Note: Some RST conversions failed. See $LOG_FILE for details." >&2
exit 1
}

View File

@ -12,3 +12,103 @@ sphinx-tabs==3.4.5 # Compatibility with docutils==0.17.0
myst-parser
rst-to-myst[sphinx]
sphinx-autobuild
# The officially supported versions of the following packages are their
# python3-* equivalent distributed in Ubuntu 24.04 and Debian 12
asn1crypto==1.4.0 ; python_version < '3.11'
asn1crypto==1.5.1 ; python_version >= '3.11'
Babel==2.9.1 ; python_version < '3.11' # min version = 2.6.0 (Focal with security backports)
Babel==2.10.3 ; python_version >= '3.11'
cbor2==5.4.2 ; python_version < '3.12'
cbor2==5.6.2 ; python_version >= '3.12'
chardet==4.0.0 ; python_version < '3.11' # (Jammy)
chardet==5.2.0 ; python_version >= '3.11'
cryptography==3.4.8; python_version < '3.12' # incompatibility between pyopenssl 19.0.0 and cryptography>=37.0.0
cryptography==42.0.8 ; python_version >= '3.12' # (Noble) min 41.0.7, pinning 42.0.8 for security fixes
decorator==4.4.2 ; python_version < '3.11' # (Jammy)
decorator==5.1.1 ; python_version >= '3.11'
freezegun==1.1.0 ; python_version < '3.11' # (Jammy)
freezegun==1.2.1 ; python_version >= '3.11'
geoip2==2.9.0
gevent==21.8.0 ; sys_platform != 'win32' and python_version == '3.10' # (Jammy)
gevent==22.10.2; sys_platform != 'win32' and python_version > '3.10' and python_version < '3.12'
gevent==24.2.1 ; sys_platform != 'win32' and python_version >= '3.12' # (Noble)
greenlet==1.1.2 ; sys_platform != 'win32' and python_version == '3.10' # (Jammy)
greenlet==2.0.2 ; sys_platform != 'win32' and python_version > '3.10' and python_version < '3.12'
greenlet==3.0.3 ; sys_platform != 'win32' and python_version >= '3.12' # (Noble)
idna==2.10 ; python_version < '3.12' # requests 2.25.1 depends on idna<3 and >=2.5
idna==3.6 ; python_version >= '3.12'
Jinja2==3.0.3 ; python_version <= '3.10'
Jinja2==3.1.2 ; python_version > '3.10' # (Noble) Mostly to have a wheel package
lxml==4.8.0 ; python_version <= '3.10'
lxml==4.9.3 ; python_version > '3.10' and python_version < '3.12' # min 4.9.2, pinning 4.9.3 because of missing wheels for darwin in 4.9.3
lxml==5.2.1; python_version >= '3.12' # (Noble - removed html clean)
lxml-html-clean; python_version >= '3.12' # (Noble - removed from lxml, unpinned for futur security patches)
MarkupSafe==2.0.1 ; python_version <= '3.10'
MarkupSafe==2.1.2 ; python_version > '3.10' and python_version < '3.12'
MarkupSafe==2.1.5 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package
num2words==0.5.10 ; python_version < '3.12' # (Jammy / Bookworm)
num2words==0.5.13 ; python_version >= '3.12'
ofxparse==0.21
openpyxl==3.0.9 ; python_version < '3.12'
openpyxl==3.1.2 ; python_version >= '3.12'
passlib==1.7.4 # min version = 1.7.2 (Focal with security backports)
Pillow==9.0.1 ; python_version <= '3.10'
Pillow==9.4.0 ; python_version > '3.10' and python_version < '3.12'
Pillow==10.2.0 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package
polib==1.1.1
psutil==5.9.0 ; python_version <= '3.10'
psutil==5.9.4 ; python_version > '3.10' and python_version < '3.12'
psutil==5.9.8 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package
psycopg2==2.9.2 ; python_version == '3.10' # (Jammy)
psycopg2==2.9.5 ; python_version == '3.11'
psycopg2==2.9.9 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package
pyopenssl==21.0.0 ; python_version < '3.12'
pyopenssl==24.1.0 ; python_version >= '3.12' # (Noble) min 23.2.0, pinned for compatibility with cryptography==42.0.8 and security patches
PyPDF2==1.26.0 ; python_version <= '3.10'
PyPDF2==2.12.1 ; python_version > '3.10'
pypiwin32 ; sys_platform == 'win32'
pyserial==3.5
python-dateutil==2.8.1 ; python_version < '3.11'
python-dateutil==2.8.2 ; python_version >= '3.11'
python-ldap==3.4.0 ; sys_platform != 'win32' and python_version < '3.12' # min version = 3.2.0 (Focal with security backports)
python-ldap==3.4.4 ; sys_platform != 'win32' and python_version >= '3.12' # (Noble) Mostly to have a wheel package
python-stdnum==1.17 ; python_version < '3.11' # (jammy)
python-stdnum==1.19 ; python_version >= '3.11'
pytz # no version pinning to avoid OS perturbations
pyusb==1.2.1
qrcode==7.3.1 ; python_version < '3.11' # (jammy)
qrcode==7.4.2 ; python_version >= '3.11'
reportlab==3.6.8 ; python_version <= '3.10'
reportlab==3.6.12 ; python_version > '3.10' and python_version < '3.12'
reportlab==4.1.0 ; python_version >= '3.12' # (Noble) Mostly to have a wheel package
requests==2.25.1 ; python_version < '3.11' # versions < 2.25 aren't compatible w/ urllib3 1.26. Bullseye = 2.25.1. min version = 2.22.0 (Focal)
requests==2.31.0 ; python_version >= '3.11' # (Noble)
rjsmin==1.1.0 ; python_version < '3.11' # (jammy)
rjsmin==1.2.0 ; python_version >= '3.11'
rl-renderPM==4.0.3 ; sys_platform == 'win32' and python_version >= '3.12' # Needed by reportlab 4.1.0 but included in deb package
urllib3==1.26.5 ; python_version < '3.12' # indirect / min version = 1.25.8 (Focal with security backports)
urllib3==2.0.7 ; python_version >= '3.12' # (Noble) Compatibility with cryptography
vobject==0.9.6.1
Werkzeug==2.0.2 ; python_version <= '3.10'
Werkzeug==2.2.2 ; python_version > '3.10' and python_version < '3.12'
Werkzeug==3.0.1 ; python_version >= '3.12' # (Noble) Avoid deprecation warnings
xlrd==1.2.0 ; python_version < '3.12' # (jammy)
xlrd==2.0.1 ; python_version >= '3.12'
XlsxWriter==3.0.2 ; python_version < '3.12' # (jammy)
XlsxWriter==3.1.9 ; python_version >= '3.12'
xlwt==1.3.0
zeep==4.1.0 ; python_version < '3.11' # (jammy)
zeep==4.2.1 ; python_version >= '3.11'
python-dotenv==1.0.1; python_version > '3.10'
python_docx_replace ; python_version > '3.10'
python-docx ; python_version > '3.10'
html2text ; python_version > '3.10'
docx ; python_version > '3.10'
dropbox ; python_version > '3.10'
pyncclient ; python_version > '3.10'
nextcloud-api-wrapper ; python_version > '3.10'
boto3 ; python_version > '3.10'
paramiko ; python_version > '3.10'
proxmoxer ; python_version > '3.10'
requests ; python_version > '3.10'
google_auth ; python_version > '3.10'