From 97ec32ab2b5bca83e53e47d0b5839c125bc532b2 Mon Sep 17 00:00:00 2001 From: shriraj pawar Date: Mon, 4 May 2026 23:53:16 +0530 Subject: [PATCH 1/3] Fix __init__.py labels in rendered diff listings --- .github/workflows/docs-build.yaml | 4 ++ src/ethereum_spec_tools/docc.py | 25 ++++++++--- tests/spec_tools/test_docc.py | 74 +++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 tests/spec_tools/test_docc.py diff --git a/.github/workflows/docs-build.yaml b/.github/workflows/docs-build.yaml index a961754914..d50d70421a 100644 --- a/.github/workflows/docs-build.yaml +++ b/.github/workflows/docs-build.yaml @@ -25,6 +25,7 @@ on: - "src/ethereum/**" - "src/ethereum_spec_tools/docc.py" - "src/ethereum_spec_tools/forks.py" + - "tests/spec_tools/**" - "static/**" - "docs/**" - "packages/testing/**" @@ -235,6 +236,9 @@ jobs: - uses: ./.github/actions/setup-uv + - name: Run docc diff regression test + run: uv run --group test --group doc pytest tests/spec_tools/test_docc.py + - name: Build spec documentation run: just docs-spec env: diff --git a/src/ethereum_spec_tools/docc.py b/src/ethereum_spec_tools/docc.py index c93a943eeb..072af4263c 100644 --- a/src/ethereum_spec_tools/docc.py +++ b/src/ethereum_spec_tools/docc.py @@ -148,6 +148,18 @@ def _diff_path(before: Hardfork, after: Hardfork) -> PurePath: return PurePath("diffs") / before.short_name / after.short_name +def _diff_source_paths( + diff_root: PurePath, path: PurePath +) -> Tuple[PurePath, PurePath]: + listing_path = diff_root / path + output_path = listing_path + + if path.name == "__init__.py": + output_path = output_path.with_name("index") + + return listing_path, output_path + + class _ForkOrder: forks: List[PurePath] diffs: List[PurePath] @@ -295,16 +307,16 @@ def discover(self, known: FrozenSet[T]) -> Iterator[Source]: assert before_source or after_source - if path.name == "__init__.py": - path = path.with_name("index") - - output_path = _diff_path(before, after) / path + relative_path, output_path = _diff_source_paths( + _diff_path(before, after), path + ) yield DiffSource( before.name, before_source, after.name, after_source, + relative_path, output_path, ) @@ -327,6 +339,7 @@ class DiffSource(Generic[S], Source, Listable): after_name: str after: Optional[S] + _relative_path: PurePath _output_path: PurePath def __init__( @@ -335,6 +348,7 @@ def __init__( before: Optional[S], after_name: str, after: Optional[S], + relative_path: PurePath, output_path: PurePath, ) -> None: self.before_name = before_name @@ -343,6 +357,7 @@ def __init__( self.after_name = after_name self.after = after + self._relative_path = relative_path self._output_path = output_path @property @@ -357,7 +372,7 @@ def relative_path(self) -> Optional[PurePath]: """ Path to the Source (if one exists) relative to the project root. """ - return None + return self._relative_path @property def output_path(self) -> PurePath: diff --git a/tests/spec_tools/test_docc.py b/tests/spec_tools/test_docc.py new file mode 100644 index 0000000000..d657372c42 --- /dev/null +++ b/tests/spec_tools/test_docc.py @@ -0,0 +1,74 @@ +from pathlib import PurePath + +from docc.context import Context +from docc.plugins import html +from docc.plugins.listing import Listing, ListingNode, render_html +from docc.source import Source + +from ethereum_spec_tools.docc import DiffSource, _diff_source_paths + + +def _render_listing_label(source: DiffSource[Source]) -> str: + listing = Listing() + listing.add_source(source) + + context = Context({Source: source, Listing: listing}) + root = html.HTMLRoot(context) + render_html(context, root, ListingNode(True)) + + for child in root.children: + if isinstance(child, html.HTMLTag): + return "".join(child._to_element().itertext()).strip() + + raise AssertionError("listing render produced no HTML output") + + +def test_diff_source_renders_init_label_but_writes_to_index() -> None: + relative_path, output_path = _diff_source_paths( + PurePath("diffs/frontier/homestead"), + PurePath("vm/__init__.py"), + ) + + old_shape_source = DiffSource( + "frontier", + None, + "homestead", + None, + PurePath("diffs/frontier/homestead/vm/index"), + PurePath("diffs/frontier/homestead/vm/index"), + ) + diff_source = DiffSource( + "frontier", + None, + "homestead", + None, + relative_path, + output_path, + ) + + assert _render_listing_label(old_shape_source) == "index" + assert _render_listing_label(diff_source) == "__init__.py" + assert diff_source.output_path == PurePath( + "diffs/frontier/homestead/vm/index" + ) + assert diff_source.index_dir == PurePath("diffs/frontier/homestead/vm") + + +def test_diff_source_renders_normal_module_label() -> None: + relative_path, output_path = _diff_source_paths( + PurePath("diffs/frontier/homestead"), + PurePath("vm/gas.py"), + ) + + diff_source = DiffSource( + "frontier", + None, + "homestead", + None, + relative_path, + output_path, + ) + + assert _render_listing_label(diff_source) == "gas.py" + assert diff_source.output_path == diff_source.relative_path + assert diff_source.index_dir is None From 8d237d7b6f8fc9b192c0b405c7e776152e621cdd Mon Sep 17 00:00:00 2001 From: shriraj pawar Date: Tue, 5 May 2026 07:50:48 +0530 Subject: [PATCH 2/3] fix for ci issues --- docs/scripts/gen_test_case_reference.py | 1 + tests/spec_tools/test_docc.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/scripts/gen_test_case_reference.py b/docs/scripts/gen_test_case_reference.py index ba2a00b38f..67cd931fcd 100644 --- a/docs/scripts/gen_test_case_reference.py +++ b/docs/scripts/gen_test_case_reference.py @@ -58,6 +58,7 @@ "--checklist-doc-gen", "--skip-index", "--ignore=tests/ported_static", + "--ignore=tests/spec_tools", "-m", "not blockchain_test_engine", "-s", diff --git a/tests/spec_tools/test_docc.py b/tests/spec_tools/test_docc.py index d657372c42..8691c36d36 100644 --- a/tests/spec_tools/test_docc.py +++ b/tests/spec_tools/test_docc.py @@ -1,3 +1,5 @@ +"""Regression tests for diff listing labels in docc-generated pages.""" + from pathlib import PurePath from docc.context import Context @@ -9,6 +11,7 @@ def _render_listing_label(source: DiffSource[Source]) -> str: + """Render a leaf listing for a single source and return its label text.""" listing = Listing() listing.add_source(source) @@ -24,12 +27,13 @@ def _render_listing_label(source: DiffSource[Source]) -> str: def test_diff_source_renders_init_label_but_writes_to_index() -> None: + """Render `__init__.py` in Browse while preserving the `index` output.""" relative_path, output_path = _diff_source_paths( PurePath("diffs/frontier/homestead"), PurePath("vm/__init__.py"), ) - old_shape_source = DiffSource( + old_shape_source: DiffSource[Source] = DiffSource( "frontier", None, "homestead", @@ -37,7 +41,7 @@ def test_diff_source_renders_init_label_but_writes_to_index() -> None: PurePath("diffs/frontier/homestead/vm/index"), PurePath("diffs/frontier/homestead/vm/index"), ) - diff_source = DiffSource( + diff_source: DiffSource[Source] = DiffSource( "frontier", None, "homestead", @@ -55,12 +59,13 @@ def test_diff_source_renders_init_label_but_writes_to_index() -> None: def test_diff_source_renders_normal_module_label() -> None: + """Render normal modules with their filename unchanged.""" relative_path, output_path = _diff_source_paths( PurePath("diffs/frontier/homestead"), PurePath("vm/gas.py"), ) - diff_source = DiffSource( + diff_source: DiffSource[Source] = DiffSource( "frontier", None, "homestead", From 87691aa68f0d9f2acb21ca0e3885122f80540188 Mon Sep 17 00:00:00 2001 From: shriraj pawar Date: Wed, 6 May 2026 08:57:51 +0530 Subject: [PATCH 3/3] Address docc diff review follow-ups --- .github/workflows/docs-build.yaml | 2 +- pyproject.toml | 2 +- src/ethereum_spec_tools/docc.py | 4 ++++ tests/spec_tools/test_docc.py | 18 +++++++++++------- uv.lock | 6 ++++++ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docs-build.yaml b/.github/workflows/docs-build.yaml index d50d70421a..f059b90543 100644 --- a/.github/workflows/docs-build.yaml +++ b/.github/workflows/docs-build.yaml @@ -237,7 +237,7 @@ jobs: - uses: ./.github/actions/setup-uv - name: Run docc diff regression test - run: uv run --group test --group doc pytest tests/spec_tools/test_docc.py + run: uv run --group test pytest tests/spec_tools/test_docc.py - name: Build spec documentation run: just docs-spec diff --git a/pyproject.toml b/pyproject.toml index b89580d7ac..c4c45856af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -211,6 +211,7 @@ test = [ "requests-cache>=1.2.1,<2", "libcst>=1.8,<2", "ethereum-execution-testing", + { include-group = "doc" }, ] lint = [ "codespell==2.4.1", @@ -253,7 +254,6 @@ dev = [ { include-group = "test" }, { include-group = "lint" }, { include-group = "actionlint" }, - { include-group = "doc" }, { include-group = "mkdocs" }, "ethereum-execution[optimized]", "psutil>=7.2.2", diff --git a/src/ethereum_spec_tools/docc.py b/src/ethereum_spec_tools/docc.py index 072af4263c..0db0b08c2a 100644 --- a/src/ethereum_spec_tools/docc.py +++ b/src/ethereum_spec_tools/docc.py @@ -332,6 +332,10 @@ def discover(self, known: FrozenSet[T]) -> Iterator[Source]: class DiffSource(Generic[S], Source, Listable): """ A source that represents the difference between two other sources. + + This source is synthetic, so its `relative_path` is synthetic too. We keep + it separate from `output_path` because `docc` directory listings use + `relative_path` as the visible label. """ before_name: str diff --git a/tests/spec_tools/test_docc.py b/tests/spec_tools/test_docc.py index 8691c36d36..06d00f53f3 100644 --- a/tests/spec_tools/test_docc.py +++ b/tests/spec_tools/test_docc.py @@ -4,24 +4,28 @@ from docc.context import Context from docc.plugins import html -from docc.plugins.listing import Listing, ListingNode, render_html +from docc.plugins.listing import ListingNode, ListingSource, render_html from docc.source import Source from ethereum_spec_tools.docc import DiffSource, _diff_source_paths def _render_listing_label(source: DiffSource[Source]) -> str: - """Render a leaf listing for a single source and return its label text.""" - listing = Listing() - listing.add_source(source) + """Render a leaf listing for a single source and return its leaf label.""" + listing = ListingSource( + PurePath("diffs/frontier/homestead/vm"), + PurePath("diffs/frontier/homestead/vm/index"), + {source}, + ) - context = Context({Source: source, Listing: listing}) + context = Context({Source: listing}) root = html.HTMLRoot(context) - render_html(context, root, ListingNode(True)) + render_html(context, root, ListingNode({source})) for child in root.children: if isinstance(child, html.HTMLTag): - return "".join(child._to_element().itertext()).strip() + text = "".join(child._to_element().itertext()).strip() + return PurePath(text).name raise AssertionError("listing render produced no HTML output") diff --git a/uv.lock b/uv.lock index 9c6ba50bf7..2c0ce8c87a 100644 --- a/uv.lock +++ b/uv.lock @@ -927,10 +927,13 @@ mkdocs = [ { name = "pyspelling" }, ] test = [ + { name = "docc" }, { name = "ethereum-execution-testing" }, { name = "filelock" }, + { name = "fladrif" }, { name = "gitpython" }, { name = "libcst" }, + { name = "mistletoe" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-xdist" }, @@ -1035,10 +1038,13 @@ mkdocs = [ { name = "pyspelling", specifier = ">=2.8.2,<3" }, ] test = [ + { name = "docc", specifier = ">=0.6.0,<0.7.0" }, { name = "ethereum-execution-testing", editable = "packages/testing" }, { name = "filelock", specifier = ">=3.15.1,<4" }, + { name = "fladrif", specifier = ">=0.2.0,<0.3.0" }, { name = "gitpython", specifier = ">=3.1.0,<3.2" }, { name = "libcst", specifier = ">=1.8,<2" }, + { name = "mistletoe", specifier = ">=1.5.0,<2" }, { name = "pytest", specifier = ">=8,<9" }, { name = "pytest-cov", specifier = ">=4.1.0,<5" }, { name = "pytest-xdist", specifier = ">=3.3.1,<4" },