diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5d6735..ba25a01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.11", "3.12"] + python-version: ["3.11", "3.12", "3.13"] + sphinx-version: ["6", "7", "8"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -34,16 +35,17 @@ jobs: run : | python -m pip install --upgrade pip pip install -e.[testing] + pip install "sphinx>=${{ matrix.sphinx-version }},<${{ matrix.sphinx-version == '6' && '7' || matrix.sphinx-version == '7' && '8' || '9' }}" - name: Run pytest run: | pytest --durations=10 --cov=sphinx_exercise --cov-report=xml --cov-report=term-missing - name: Create cov run: coverage xml - name: Upload to Codecov - if: false && (matrix.python-version == '3.11') + if: false && (matrix.python-version == '3.11' && matrix.sphinx-version == '8') uses: codecov/codecov-action@v4 with: - name: sphinx-exercise-pytest-py3.11 + name: sphinx-exercise-pytest-py3.11-sphinx8 token: "${{ secrets.CODECOV_TOKEN }}" flags: pytests file: ./coverage.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index ebcbd16..61fbbc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ # Changelog +## [v1.0.1](https://github.com/executablebooks/sphinx-exercise/tree/v1.0.1) (2024-07-01) + +### Improved 👌 + +- Updates to testing infrastructure and minor bug fixes +- Fixed JupyterBuilder handling in test suite + +## [v1.0.0](https://github.com/executablebooks/sphinx-exercise/tree/v1.0.0) (2024-01-15) + +### Improved 👌 + +- Added support for Sphinx 7 +- Updated build and CI infrastructure +- Fixed deprecation warnings for Sphinx 7 compatibility +- Improved type hints throughout codebase +- Fixed doctree node mutation issues by copying nodes +- Updated codecov to version 3 +- Pinned matplotlib to 3.7.* for testing stability + ## [v0.4.1](https://github.com/executablebooks/sphinx-exercise/tree/v0.4.1) (2023-1-23) ### Improved 👌 diff --git a/MANIFEST.in b/MANIFEST.in index 7edfdf4..b950eb7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,4 +8,4 @@ recursive-include sphinx_exercise *.css recursive-include sphinx_exercise *.json recursive-include sphinx_exercise *.mo recursive-include sphinx_exercise *.po -recursive-include sphinx_exercise *.py \ No newline at end of file +recursive-include sphinx_exercise *.py diff --git a/pyproject.toml b/pyproject.toml index 13e41f7..f26001b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "A Sphinx extension for producing exercises and solutions." readme = "README.md" license = { file = "LICENSE" } -requires-python = ">=3.9" +requires-python = ">=3.11" authors = [ { name = "QuantEcon", email = "admin@quantecon.org" }, ] @@ -22,10 +22,9 @@ classifiers = [ "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Documentation", "Topic :: Documentation :: Sphinx", "Topic :: Software Development :: Documentation", @@ -34,7 +33,7 @@ classifiers = [ ] dependencies = [ "sphinx-book-theme", - "sphinx>=5", + "sphinx>=6.1", ] [project.optional-dependencies] @@ -49,20 +48,20 @@ code_style = [ "pre-commit", ] rtd = [ - "myst-nb~=1.0.0", + "myst-nb>=1.1.0", "sphinx-book-theme", "sphinx_togglebutton", - "sphinx>=5,<8", + "sphinx>=6.1,<9", ] testing = [ "beautifulsoup4", "coverage", - "matplotlib==3.8.*", - "myst-nb~=1.0.0", + "matplotlib>=3.8", + "myst-nb>=1.1.0", "pytest-cov", "pytest-regressions", - "pytest~=8.0.0", - "sphinx>=5,<8", + "pytest>=8.0", + "sphinx>=6.1,<9", "texsoup", "defusedxml", # Required by sphinx-testing ] diff --git a/sphinx_exercise/__init__.py b/sphinx_exercise/__init__.py index 60ed3b9..837a3b4 100644 --- a/sphinx_exercise/__init__.py +++ b/sphinx_exercise/__init__.py @@ -9,7 +9,6 @@ __version__ = "1.0.1" -import os from pathlib import Path from typing import Any, Dict, Set, Union, cast from sphinx.config import Config @@ -71,6 +70,7 @@ # Callback Functions + def purge_exercises(app: Sphinx, env: BuildEnvironment, docname: str) -> None: """Purge sphinx_exercise registry""" @@ -214,9 +214,9 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_css_file("exercise.css") # add translations - package_dir = os.path.abspath(os.path.dirname(__file__)) - locale_dir = os.path.join(package_dir, "translations", "locales") - app.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir) + package_dir = Path(__file__).parent.resolve() + locale_dir = package_dir / "translations" / "locales" + app.add_message_catalog(MESSAGE_CATALOG_NAME, str(locale_dir)) return { "version": "builtin", diff --git a/sphinx_exercise/directive.py b/sphinx_exercise/directive.py index 2db6d5b..0302c65 100644 --- a/sphinx_exercise/directive.py +++ b/sphinx_exercise/directive.py @@ -8,28 +8,30 @@ :licences: see LICENSE for details """ +from pathlib import Path from typing import List -from docutils.nodes import Node -from sphinx.util.docutils import SphinxDirective +from docutils import nodes +from docutils.nodes import Node from docutils.parsers.rst import directives +from sphinx.locale import get_translation +from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective + from .nodes import ( - exercise_node, - exercise_enumerable_node, exercise_end_node, + exercise_enumerable_node, + exercise_node, + exercise_subtitle, + exercise_title, + solution_end_node, solution_node, solution_start_node, - solution_end_node, - exercise_title, - exercise_subtitle, solution_title, ) -from docutils import nodes -from sphinx.util import logging logger = logging.getLogger(__name__) -from sphinx.locale import get_translation MESSAGE_CATALOG_NAME = "exercise" translate = get_translation(MESSAGE_CATALOG_NAME) @@ -40,7 +42,7 @@ def duplicate_labels(self, label): if not label == "" and label in self.env.sphinx_exercise_registry.keys(): docpath = self.env.doc2path(self.env.docname) - path = docpath[: docpath.rfind(".")] + path = str(Path(docpath).with_suffix("")) other_path = self.env.doc2path( self.env.sphinx_exercise_registry[label]["docname"] ) diff --git a/sphinx_exercise/nodes.py b/sphinx_exercise/nodes.py index c34fd36..263428a 100644 --- a/sphinx_exercise/nodes.py +++ b/sphinx_exercise/nodes.py @@ -13,13 +13,12 @@ from docutils import nodes as docutil_nodes from sphinx import addnodes as sphinx_nodes from sphinx.writers.latex import LaTeXTranslator +from sphinx.locale import get_translation from .latex import LaTeXMarkup logger = logging.getLogger(__name__) LaTeX = LaTeXMarkup() - -from sphinx.locale import get_translation MESSAGE_CATALOG_NAME = "exercise" translate = get_translation(MESSAGE_CATALOG_NAME) @@ -54,7 +53,10 @@ class solution_end_node(docutil_nodes.Admonition, docutil_nodes.Element): class exercise_title(docutil_nodes.title): def default_title(self): title_text = self.children[0].astext() - if title_text == f"{translate('Exercise')}" or title_text == f"{translate('Exercise')} %s": + if ( + title_text == f"{translate('Exercise')}" + or title_text == f"{translate('Exercise')} %s" + ): return True else: return False diff --git a/sphinx_exercise/post_transforms.py b/sphinx_exercise/post_transforms.py index 6b477b5..97b79d7 100644 --- a/sphinx_exercise/post_transforms.py +++ b/sphinx_exercise/post_transforms.py @@ -1,3 +1,5 @@ +from pathlib import Path + import sphinx.addnodes as sphinx_nodes from sphinx.transforms.post_transforms import SphinxPostTransform from sphinx.util import logging @@ -195,7 +197,7 @@ def run(self): except AttributeError: docname = self.env.docname # for builder such as JupyterBuilder that don't support current_docname docpath = self.env.doc2path(docname) - path = docpath[: docpath.rfind(".")] + path = str(Path(docpath).with_suffix("")) msg = f"undefined label: {target_label}" logger.warning(msg, location=path, color="red") return diff --git a/sphinx_exercise/translations/_convert.py b/sphinx_exercise/translations/_convert.py index 882f954..dc88dd9 100644 --- a/sphinx_exercise/translations/_convert.py +++ b/sphinx_exercise/translations/_convert.py @@ -1,10 +1,10 @@ import json -import os from pathlib import Path import subprocess MESSAGE_CATALOG_NAME = "exercise" + def convert_json(folder=None): folder = folder or Path(__file__).parent @@ -19,7 +19,13 @@ def convert_json(folder=None): english = data[0]["text"] for item in data[1:]: language = item["symbol"] - out_path = folder / "locales" / language / "LC_MESSAGES" / f"{MESSAGE_CATALOG_NAME}.po" + out_path = ( + folder + / "locales" + / language + / "LC_MESSAGES" + / f"{MESSAGE_CATALOG_NAME}.po" + ) if not out_path.parent.exists(): out_path.parent.mkdir(parents=True) if not out_path.exists(): @@ -47,9 +53,9 @@ def convert_json(folder=None): subprocess.check_call( [ "msgfmt", - os.path.abspath(path), + str(path.resolve()), "-o", - os.path.abspath(path.parent / f"{MESSAGE_CATALOG_NAME}.mo"), + str((path.parent / f"{MESSAGE_CATALOG_NAME}.mo").resolve()), ] ) diff --git a/tests/books/test-gateddirective/solution-exercise-gated.md b/tests/books/test-gateddirective/solution-exercise-gated.md index 7fb42c1..0929241 100644 --- a/tests/books/test-gateddirective/solution-exercise-gated.md +++ b/tests/books/test-gateddirective/solution-exercise-gated.md @@ -42,7 +42,7 @@ axs[0].set_xlabel('time') axs[0].set_ylabel('s1 and s2') axs[0].grid(True) -cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt) axs[1].set_ylabel('coherence') fig.tight_layout() @@ -87,7 +87,7 @@ axs[0].set_xlabel('time') axs[0].set_ylabel('s1 and s2') axs[0].grid(True) -cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt) axs[1].set_ylabel('coherence') fig.tight_layout() diff --git a/tests/books/test-gateddirective/solution-exercise.md b/tests/books/test-gateddirective/solution-exercise.md index 176bb6f..a223099 100644 --- a/tests/books/test-gateddirective/solution-exercise.md +++ b/tests/books/test-gateddirective/solution-exercise.md @@ -42,7 +42,7 @@ axs[0].set_xlabel('time') axs[0].set_ylabel('s1 and s2') axs[0].grid(True) -cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt) axs[1].set_ylabel('coherence') fig.tight_layout() @@ -85,7 +85,7 @@ axs[0].set_xlabel('time') axs[0].set_ylabel('s1 and s2') axs[0].grid(True) -cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt) axs[1].set_ylabel('coherence') fig.tight_layout() diff --git a/tests/conftest.py b/tests/conftest.py index df54536..3755088 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,8 @@ def rootdir(tmpdir): from sphinx.testing.path import path - src = path(__file__).parent.absolute() / "books" + # In Sphinx 6, path objects don't have .absolute() method, but they are already absolute + src = path(__file__).parent / "books" dst = tmpdir.join("books") shutil.copytree(src, dst) yield path(dst) @@ -94,6 +95,10 @@ class FileRegression: (r"original_uri=\"[^\"]*\"\s", ""), # TODO: Remove when support for Sphinx<7.2 is dropped ("Link to", "Permalink to"), + # Strip ipykernel process IDs (temporary directory paths) + (r"ipykernel_\d+", "ipykernel_XXXXX"), + # Normalize matplotlib image hashes (platform/version dependent) + (r"[a-f0-9]{64}\.png", "IMAGEHASH.png"), ) def __init__(self, file_regression): diff --git a/tests/test_exercise_references.py b/tests/test_exercise_references.py index 4ba56a9..5b419e7 100644 --- a/tests/test_exercise_references.py +++ b/tests/test_exercise_references.py @@ -1,5 +1,8 @@ from bs4 import BeautifulSoup import pytest +import sphinx + +SPHINX_VERSION = f".sphinx{sphinx.version_info[0]}" @pytest.mark.sphinx("html", testroot="mybook") @@ -35,7 +38,7 @@ def test_reference(app, idir, file_regression): excs += f"{ref}\n" file_regression.check( - str(excs[:-1]), basename=idir.split(".")[0], extension=".html" + str(excs[:-1]), basename=idir.split(".")[0], extension=f"{SPHINX_VERSION}.html" ) diff --git a/tests/test_exercise_references/_enum_numref_mathtitle.sphinx6.html b/tests/test_exercise_references/_enum_numref_mathtitle.sphinx6.html new file mode 100644 index 0000000..5d17801 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_mathtitle.sphinx6.html @@ -0,0 +1,4 @@ +
This is a reference Exercise 1.
+This is a second reference some text 1.
+This is a third reference some text 1.
+This is a fourth reference some text Exercise.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_mathtitle.sphinx7.html b/tests/test_exercise_references/_enum_numref_mathtitle.sphinx7.html new file mode 100644 index 0000000..5d17801 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_mathtitle.sphinx7.html @@ -0,0 +1,4 @@ +This is a reference Exercise 1.
+This is a second reference some text 1.
+This is a third reference some text 1.
+This is a fourth reference some text Exercise.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_mathtitle.sphinx8.html b/tests/test_exercise_references/_enum_numref_mathtitle.sphinx8.html new file mode 100644 index 0000000..16a3e5b --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_mathtitle.sphinx8.html @@ -0,0 +1,6 @@ +This is a reference Exercise 1.
+This is a second reference some text 1.
+This is a third reference some text 1.
+This is a fourth reference some text Exercise.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_notitle.sphinx6.html b/tests/test_exercise_references/_enum_numref_notitle.sphinx6.html new file mode 100644 index 0000000..90a5c54 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_notitle.sphinx6.html @@ -0,0 +1,4 @@ +This is a reference Exercise 2.
+This is a second reference some text 2.
+This is a third reference some text 2.
+This is a fourth reference some text Exercise.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_notitle.sphinx7.html b/tests/test_exercise_references/_enum_numref_notitle.sphinx7.html new file mode 100644 index 0000000..90a5c54 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_notitle.sphinx7.html @@ -0,0 +1,4 @@ +This is a reference Exercise 2.
+This is a second reference some text 2.
+This is a third reference some text 2.
+This is a fourth reference some text Exercise.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_notitle.sphinx8.html b/tests/test_exercise_references/_enum_numref_notitle.sphinx8.html new file mode 100644 index 0000000..eb699a4 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_notitle.sphinx8.html @@ -0,0 +1,6 @@ +This is a reference Exercise 2.
+This is a second reference some text 2.
+This is a third reference some text 2.
+This is a fourth reference some text Exercise.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_placeholders.sphinx6.html b/tests/test_exercise_references/_enum_numref_placeholders.sphinx6.html new file mode 100644 index 0000000..07f2a5a --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_placeholders.sphinx6.html @@ -0,0 +1,10 @@ +This reference does not include math some 3 text %s test Exercise.
+This reference does not include math some 3 text %s test.
+This reference does not include math some Exercise text %s test.
+This reference does not include math some Exercise text 3 test.
+This reference does not include math some 3 text test.
+This reference includes math some 1 text %s test Exercise.
+This reference includes math some 1 text %s test.
+This reference includes math some Exercise text %s test.
+This reference includes math some Exercise text 1 test.
+This reference includes math some 1 text test.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_placeholders.sphinx7.html b/tests/test_exercise_references/_enum_numref_placeholders.sphinx7.html new file mode 100644 index 0000000..07f2a5a --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_placeholders.sphinx7.html @@ -0,0 +1,10 @@ +This reference does not include math some 3 text %s test Exercise.
+This reference does not include math some 3 text %s test.
+This reference does not include math some Exercise text %s test.
+This reference does not include math some Exercise text 3 test.
+This reference does not include math some 3 text test.
+This reference includes math some 1 text %s test Exercise.
+This reference includes math some 1 text %s test.
+This reference includes math some Exercise text %s test.
+This reference includes math some Exercise text 1 test.
+This reference includes math some 1 text test.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_placeholders.sphinx8.html b/tests/test_exercise_references/_enum_numref_placeholders.sphinx8.html new file mode 100644 index 0000000..9bc2e26 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_placeholders.sphinx8.html @@ -0,0 +1,12 @@ +This reference does not include math some 3 text %s test Exercise.
+This reference does not include math some 3 text %s test.
+This reference does not include math some Exercise text %s test.
+This reference does not include math some Exercise text 3 test.
+This reference does not include math some 3 text test.
+This reference includes math some 1 text %s test Exercise.
+This reference includes math some 1 text %s test.
+This reference includes math some Exercise text %s test.
+This reference includes math some Exercise text 1 test.
+This reference includes math some 1 text test.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_title.sphinx6.html b/tests/test_exercise_references/_enum_numref_title.sphinx6.html new file mode 100644 index 0000000..c2a4496 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_title.sphinx6.html @@ -0,0 +1,4 @@ +This is a reference Exercise 3.
+This is a second reference some text 3.
+This is a third reference some text 3.
+This is a fourth reference some text Exercise.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_title.sphinx7.html b/tests/test_exercise_references/_enum_numref_title.sphinx7.html new file mode 100644 index 0000000..c2a4496 --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_title.sphinx7.html @@ -0,0 +1,4 @@ +This is a reference Exercise 3.
+This is a second reference some text 3.
+This is a third reference some text 3.
+This is a fourth reference some text Exercise.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_numref_title.sphinx8.html b/tests/test_exercise_references/_enum_numref_title.sphinx8.html new file mode 100644 index 0000000..e3e637c --- /dev/null +++ b/tests/test_exercise_references/_enum_numref_title.sphinx8.html @@ -0,0 +1,6 @@ +This is a reference Exercise 3.
+This is a second reference some text 3.
+This is a third reference some text 3.
+This is a fourth reference some text Exercise.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_mathtitle.sphinx6.html b/tests/test_exercise_references/_enum_ref_mathtitle.sphinx6.html new file mode 100644 index 0000000..508253a --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_mathtitle.sphinx6.html @@ -0,0 +1,2 @@ +This is a reference Exercise 1.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_mathtitle.sphinx7.html b/tests/test_exercise_references/_enum_ref_mathtitle.sphinx7.html new file mode 100644 index 0000000..508253a --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_mathtitle.sphinx7.html @@ -0,0 +1,2 @@ +This is a reference Exercise 1.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_mathtitle.sphinx8.html b/tests/test_exercise_references/_enum_ref_mathtitle.sphinx8.html new file mode 100644 index 0000000..e12e85d --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_mathtitle.sphinx8.html @@ -0,0 +1,4 @@ +This is a reference Exercise 1.
+This is a second reference some text.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_notitle.sphinx6.html b/tests/test_exercise_references/_enum_ref_notitle.sphinx6.html new file mode 100644 index 0000000..972f9f9 --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_notitle.sphinx6.html @@ -0,0 +1,2 @@ +This is a reference Exercise 2.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_notitle.sphinx7.html b/tests/test_exercise_references/_enum_ref_notitle.sphinx7.html new file mode 100644 index 0000000..972f9f9 --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_notitle.sphinx7.html @@ -0,0 +1,2 @@ +This is a reference Exercise 2.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_notitle.sphinx8.html b/tests/test_exercise_references/_enum_ref_notitle.sphinx8.html new file mode 100644 index 0000000..2f1474c --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_notitle.sphinx8.html @@ -0,0 +1,4 @@ +This is a reference Exercise 2.
+This is a second reference some text.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_title.sphinx6.html b/tests/test_exercise_references/_enum_ref_title.sphinx6.html new file mode 100644 index 0000000..5235a0a --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_title.sphinx6.html @@ -0,0 +1,2 @@ +This is a reference Exercise 3.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_title.sphinx7.html b/tests/test_exercise_references/_enum_ref_title.sphinx7.html new file mode 100644 index 0000000..5235a0a --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_title.sphinx7.html @@ -0,0 +1,2 @@ +This is a reference Exercise 3.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_enum_ref_title.sphinx8.html b/tests/test_exercise_references/_enum_ref_title.sphinx8.html new file mode 100644 index 0000000..53e83f8 --- /dev/null +++ b/tests/test_exercise_references/_enum_ref_title.sphinx8.html @@ -0,0 +1,4 @@ +This is a reference Exercise 3.
+This is a second reference some text.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_numref_mathtitle.sphinx6.html b/tests/test_exercise_references/_unenum_numref_mathtitle.sphinx6.html new file mode 100644 index 0000000..93876d4 --- /dev/null +++ b/tests/test_exercise_references/_unenum_numref_mathtitle.sphinx6.html @@ -0,0 +1,4 @@ +This is a reference unen-exc-label-math.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-label-math.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-label-math.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-notitle.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-notitle.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-notitle.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-label.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-label.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference unen-exc-label.
This is a second reference some text %s.
This is a third reference some text {number}.
This is a fourth reference some text {name}.
This is a reference Exercise.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_mathtitle.sphinx7.html b/tests/test_exercise_references/_unenum_ref_mathtitle.sphinx7.html new file mode 100644 index 0000000..a4a4bf8 --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_mathtitle.sphinx7.html @@ -0,0 +1,2 @@ +This is a reference Exercise.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_mathtitle.sphinx8.html b/tests/test_exercise_references/_unenum_ref_mathtitle.sphinx8.html new file mode 100644 index 0000000..3a9cdc0 --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_mathtitle.sphinx8.html @@ -0,0 +1,4 @@ +This is a reference Exercise.
+This is a second reference some text.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_notitle.sphinx6.html b/tests/test_exercise_references/_unenum_ref_notitle.sphinx6.html new file mode 100644 index 0000000..c20af93 --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_notitle.sphinx6.html @@ -0,0 +1,2 @@ +This is a reference Exercise.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_notitle.sphinx7.html b/tests/test_exercise_references/_unenum_ref_notitle.sphinx7.html new file mode 100644 index 0000000..c20af93 --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_notitle.sphinx7.html @@ -0,0 +1,2 @@ +This is a reference Exercise.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_notitle.sphinx8.html b/tests/test_exercise_references/_unenum_ref_notitle.sphinx8.html new file mode 100644 index 0000000..9678f3d --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_notitle.sphinx8.html @@ -0,0 +1,4 @@ +This is a reference Exercise.
+This is a second reference some text.
+ + \ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_title.sphinx6.html b/tests/test_exercise_references/_unenum_ref_title.sphinx6.html new file mode 100644 index 0000000..50ea0dd --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_title.sphinx6.html @@ -0,0 +1,2 @@ +This is a reference Exercise.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_title.sphinx7.html b/tests/test_exercise_references/_unenum_ref_title.sphinx7.html new file mode 100644 index 0000000..50ea0dd --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_title.sphinx7.html @@ -0,0 +1,2 @@ +This is a reference Exercise.
+This is a second reference some text.
\ No newline at end of file diff --git a/tests/test_exercise_references/_unenum_ref_title.sphinx8.html b/tests/test_exercise_references/_unenum_ref_title.sphinx8.html new file mode 100644 index 0000000..f9e2427 --- /dev/null +++ b/tests/test_exercise_references/_unenum_ref_title.sphinx8.html @@ -0,0 +1,4 @@ +This is a reference Exercise.
+This is a second reference some text.
+ + \ No newline at end of file diff --git a/tests/test_gateddirective.py b/tests/test_gateddirective.py index 4a493ca..8edbe70 100644 --- a/tests/test_gateddirective.py +++ b/tests/test_gateddirective.py @@ -1,11 +1,21 @@ import os import pytest +import re import shutil import sphinx from bs4 import BeautifulSoup from sphinx.errors import ExtensionError from pathlib import Path -from sphinx.testing.util import strip_escseq + +# strip_escape_sequences was removed in Sphinx 7 +try: + from sphinx.util.console import strip_escape_sequences as strip_escseq +except ImportError: + # Fallback for Sphinx 7+: simple ANSI escape sequence stripper + def strip_escseq(text: str) -> str: + """Remove ANSI escape sequences from text.""" + return re.sub(r"\x1b\[[0-9;]*m", "", text) + SPHINX_VERSION = f".sphinx{sphinx.version_info[0]}" @@ -20,7 +30,9 @@ def test_gated_exercise_build(app, docname, file_regression): exercise_directives = soup.select("div.exercise") for idx, ed in enumerate(exercise_directives): basename = docname.split(".")[0] + f"-{idx}" - file_regression.check(str(ed), basename=basename, extension=".html") + file_regression.check( + str(ed), basename=basename, extension=f"{SPHINX_VERSION}.html" + ) @pytest.mark.sphinx("html", testroot="gateddirective") @@ -35,6 +47,7 @@ def test_gated_exercise_doctree(app, docname, get_sphinx_app_doctree): docname, resolve=False, regress=True, + sphinx_version=SPHINX_VERSION, ) diff --git a/tests/test_gateddirective/exercise-gated-0.sphinx6.html b/tests/test_gateddirective/exercise-gated-0.sphinx6.html new file mode 100644 index 0000000..efd2440 --- /dev/null +++ b/tests/test_gateddirective/exercise-gated-0.sphinx6.html @@ -0,0 +1,9 @@ +Exercise 3
+Replicate this figure using matplotlib
+
+Exercise 3
+Replicate this figure using matplotlib
+
+Exercise 3
+Replicate this figure using matplotlib
+
+Exercise 4 (Replicate Matplotlib Plot)
+
+Exercise 4 (Replicate Matplotlib Plot)
+
+Exercise 4 (Replicate Matplotlib Plot)
+
+Solution to Exercise 1
+This is a solution to Non-Gated Exercise 1
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+This is a solution to Non-Gated Exercise 1
import numpy as np
-import matplotlib.pyplot as plt
+import numpy as np
+import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
@@ -26,7 +26,7 @@
axs[0].set_ylabel('s1 and s2')
axs[0].grid(True)
-cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
axs[1].set_ylabel('coherence')
fig.tight_layout()
@@ -35,7 +35,7 @@
-
+
With some follow up text to the solution
diff --git a/tests/test_gateddirective/solution-exercise-0.sphinx8.html b/tests/test_gateddirective/solution-exercise-0.sphinx8.html new file mode 100644 index 0000000..609f4a5 --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-0.sphinx8.html @@ -0,0 +1,43 @@ +Solution to Exercise 1
+This is a solution to Non-Gated Exercise 1
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+Solution to Exercise 2 (Replicate Matplotlib Plot)
+This is a solution to Non-Gated Exercise 1
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+This is a solution to Non-Gated Exercise 1
import numpy as np
-import matplotlib.pyplot as plt
+import numpy as np
+import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
@@ -26,7 +26,7 @@
axs[0].set_ylabel('s1 and s2')
axs[0].grid(True)
-cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
axs[1].set_ylabel('coherence')
fig.tight_layout()
@@ -35,7 +35,7 @@
-
+
With some follow up text to the solution
diff --git a/tests/test_gateddirective/solution-exercise-1.sphinx8.html b/tests/test_gateddirective/solution-exercise-1.sphinx8.html new file mode 100644 index 0000000..912e9fe --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-1.sphinx8.html @@ -0,0 +1,43 @@ +Solution to Exercise 2 (Replicate Matplotlib Plot)
+This is a solution to Non-Gated Exercise 1
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+Solution to Exercise 3
+This is a solution to Gated Exercise 1
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+This is a solution to Gated Exercise 1
import numpy as np
-import matplotlib.pyplot as plt
+import numpy as np
+import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
@@ -26,7 +26,7 @@
axs[0].set_ylabel('s1 and s2')
axs[0].grid(True)
-cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
axs[1].set_ylabel('coherence')
fig.tight_layout()
@@ -35,7 +35,7 @@
-
+
With some follow up text to the solution
diff --git a/tests/test_gateddirective/solution-exercise-gated-0.sphinx8.html b/tests/test_gateddirective/solution-exercise-gated-0.sphinx8.html new file mode 100644 index 0000000..cb426ea --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-gated-0.sphinx8.html @@ -0,0 +1,43 @@ +Solution to Exercise 3
+This is a solution to Gated Exercise 1
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+Solution to Exercise 4 (Replicate Matplotlib Plot)
+This is a solution to Gated Exercise 2
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+This is a solution to Gated Exercise 2
import numpy as np
-import matplotlib.pyplot as plt
+import numpy as np
+import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
@@ -26,7 +26,7 @@
axs[0].set_ylabel('s1 and s2')
axs[0].grid(True)
-cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
axs[1].set_ylabel('coherence')
fig.tight_layout()
@@ -35,7 +35,7 @@
-
+
With some follow up text to the solution
diff --git a/tests/test_gateddirective/solution-exercise-gated-1.sphinx8.html b/tests/test_gateddirective/solution-exercise-gated-1.sphinx8.html new file mode 100644 index 0000000..76bf8b2 --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-gated-1.sphinx8.html @@ -0,0 +1,43 @@ +Solution to Exercise 4 (Replicate Matplotlib Plot)
+This is a solution to Gated Exercise 2
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Fixing random state for reproducibility
+np.random.seed(19680801)
+
+dt = 0.01
+t = np.arange(0, 30, dt)
+nse1 = np.random.randn(len(t)) # white noise 1
+nse2 = np.random.randn(len(t)) # white noise 2
+
+# Two signals with a coherent part at 10Hz and a random part
+s1 = np.sin(2 * np.pi * 10 * t) + nse1
+s2 = np.sin(2 * np.pi * 10 * t) + nse2
+
+fig, axs = plt.subplots(2, 1)
+axs[0].plot(t, s1, t, s2)
+axs[0].set_xlim(0, 2)
+axs[0].set_xlabel('time')
+axs[0].set_ylabel('s1 and s2')
+axs[0].grid(True)
+
+cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
+axs[1].set_ylabel('coherence')
+
+fig.tight_layout()
+plt.show()
+
+With some follow up text to the solution
+