Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOCS: Improve perf for HTML and PDF workflow #222

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ jobs:
.venv\Scripts\Activate.ps1
pip install --no-cache-dir -r requirements/requirements_doc.txt

# Use environment variable to keep the doctree and avoid redundant build for PDF pages
# Use environment variable speed up build for PDF pages
- name: Create HTML documentation
env:
SPHINXBUILD_KEEP_DOCTREEDIR: "1"
SPHINXBUILD_HTML_AND_PDF_WORKFLOW: "1"
run: |
.venv\Scripts\Activate.ps1
. .\doc\make.bat html --color
Expand All @@ -92,7 +92,7 @@ jobs:
# Keeping doctree could cause an issue, see https://github.com/ansys/pyaedt/pull/3844/files
- name: Create PDF documentation
env:
SPHINXBUILD_KEEP_DOCTREEDIR: "0"
SPHINXBUILD_HTML_AND_PDF_WORKFLOW: "1"
run: |
.venv\Scripts\Activate.ps1
. .\doc\make.bat pdf
Expand Down
110 changes: 56 additions & 54 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,16 @@ def copy_examples_structure(app: Sphinx, config: Config):
destination_dir = Path(app.srcdir, "examples").resolve()
logger.info(f"Copying examples structures of {EXAMPLES_DIRECTORY} into {destination_dir}.")

if os.path.exists(destination_dir):
size = directory_size(destination_dir)
logger.info(f"Directory {destination_dir} ({size} MB) already exist, removing it.")
shutil.rmtree(destination_dir, ignore_errors=True)
logger.info(f"Directory removed.")
# NOTE: Only remove the examples directory if the workflow isn't tagged as coupling HTML and PDF build.
if not bool(int(os.getenv("SPHINXBUILD_HTML_AND_PDF_WORKFLOW", "0"))):
if os.path.exists(destination_dir):
size = directory_size(destination_dir)
logger.info(f"Directory {destination_dir} ({size} MB) already exist, removing it.")
shutil.rmtree(destination_dir, ignore_errors=True)
logger.info(f"Directory removed.")

ignore_python_files = lambda _, files: [file for file in files if file.endswith(".py")]
shutil.copytree(EXAMPLES_DIRECTORY, destination_dir, ignore=ignore_python_files)
shutil.copytree(EXAMPLES_DIRECTORY, destination_dir, ignore=ignore_python_files, dirs_exist_ok=True)
logger.info(f"Copy performed.")


Expand Down Expand Up @@ -189,12 +191,13 @@ def remove_examples(app: Sphinx, exception: None | Exception):
app : sphinx.application.Sphinx
Sphinx instance containing all the configuration for the documentation build.
"""
destination_dir = Path(app.srcdir) / "examples"

size = directory_size(destination_dir)
logger.info(f"Removing directory {destination_dir} ({size} MB).")
shutil.rmtree(destination_dir, ignore_errors=True)
logger.info(f"Directory removed.")
# NOTE: Only remove the examples if the workflow isn't tagged as coupling HTML and PDF build.
if not bool(int(os.getenv("SPHINXBUILD_HTML_AND_PDF_WORKFLOW", "0"))):
destination_dir = Path(app.srcdir) / "examples"
size = directory_size(destination_dir)
logger.info(f"Removing directory {destination_dir} ({size} MB).")
shutil.rmtree(destination_dir, ignore_errors=True)
logger.info(f"Directory removed.")

def remove_doctree(app: Sphinx, exception: None | Exception):
"""Remove the .doctree directory created during the documentation build.
Expand All @@ -206,11 +209,8 @@ def remove_doctree(app: Sphinx, exception: None | Exception):
exception : None or Exception
Exception raised during the build process.
"""
# Keep the doctree to avoid creating it twice. This is typically helpful in CI/CD
# where we want to build both HTML and PDF pages.
if bool(int(os.getenv("SPHINXBUILD_KEEP_DOCTREEDIR", "0"))):
logger.info(f"Keeping directory {app.doctreedir}.")
else:
# NOTE: Only remove the doctree if the workflow isn't tagged as coupling HTML and PDF build.
if not bool(int(os.getenv("SPHINXBUILD_HTML_AND_PDF_WORKFLOW", "0"))):
size = directory_size(app.doctreedir)
logger.info(f"Removing doctree {app.doctreedir} ({size} MB).")
shutil.rmtree(app.doctreedir, ignore_errors=True)
Expand All @@ -228,43 +228,45 @@ def convert_examples_into_notebooks(app):
"05_electrothermal.py",
)

count = 0
for example in EXAMPLES:
count += 1
example_path = str(example).split("examples" + os.sep)[-1]
notebook_path = example_path.replace(".py", ".ipynb")
output = subprocess.run(
[
"jupytext",
"--to",
"ipynb",
str(example),
"--output",
str(DESTINATION_DIR / notebook_path),
],
capture_output=True,
)

if output.returncode != 0:
logger.error(f"Error converting {example} to script")
logger.error(output.stderr)

# Disable execution if required
basename = os.path.basename(example)
if basename in EXAMPLES_TO_NOT_EXECUTE:
logger.warning(f"Disable execution of example {basename}.")
with open(str(DESTINATION_DIR / notebook_path), "r") as f:
nb = nbformat.read(f, as_version=nbformat.NO_CONVERT)
if "nbsphinx" not in nb.metadata:
nb.metadata["nbsphinx"] = {}
nb.metadata["nbsphinx"]["execute"] = "never"
with open(str(DESTINATION_DIR / notebook_path), "w", encoding="utf-8") as f:
nbformat.write(nb, f)

if count == 0:
logger.warning("No python examples found to convert to scripts")
else:
logger.info(f"Converted {count} python examples to scripts")
# NOTE: Only convert the examples if the workflow isn't tagged as coupling HTML and PDF build.
if not bool(int(os.getenv("SPHINXBUILD_HTML_AND_PDF_WORKFLOW", "0"))) or app.builder.name == "html":
count = 0
for example in EXAMPLES:
count += 1
example_path = str(example).split("examples" + os.sep)[-1]
notebook_path = example_path.replace(".py", ".ipynb")
output = subprocess.run(
[
"jupytext",
"--to",
"ipynb",
str(example),
"--output",
str(DESTINATION_DIR / notebook_path),
],
capture_output=True,
)

if output.returncode != 0:
logger.error(f"Error converting {example} to script")
logger.error(output.stderr)

# Disable execution if required
basename = os.path.basename(example)
if basename in EXAMPLES_TO_NOT_EXECUTE:
logger.warning(f"Disable execution of example {basename}.")
with open(str(DESTINATION_DIR / notebook_path), "r") as f:
nb = nbformat.read(f, as_version=nbformat.NO_CONVERT)
if "nbsphinx" not in nb.metadata:
nb.metadata["nbsphinx"] = {}
nb.metadata["nbsphinx"]["execute"] = "never"
with open(str(DESTINATION_DIR / notebook_path), "w", encoding="utf-8") as f:
nbformat.write(nb, f)

if count == 0:
logger.warning("No python examples found to convert to scripts")
else:
logger.info(f"Converted {count} python examples to scripts")

def setup(app):
"""Run different hook functions during the documentation build."""
Expand Down
Loading