Skip to content
Open
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
4 changes: 4 additions & 0 deletions .github/workflows/docs-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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/**"
Expand Down Expand Up @@ -235,6 +236,9 @@ jobs:

- uses: ./.github/actions/setup-uv

- name: Run docc diff regression test
run: uv run --group test pytest tests/spec_tools/test_docc.py

Comment on lines +239 to +241
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it CI failed because docc wasn't part of the test group? Would it make more sense to just add docc to the test group?

- name: Build spec documentation
run: just docs-spec
env:
Expand Down
1 change: 1 addition & 0 deletions docs/scripts/gen_test_case_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"--checklist-doc-gen",
"--skip-index",
"--ignore=tests/ported_static",
"--ignore=tests/spec_tools",
"-m",
"not blockchain_test_engine",
"-s",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
29 changes: 24 additions & 5 deletions src/ethereum_spec_tools/docc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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,
)

Expand All @@ -320,13 +332,18 @@ 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
before: Optional[S]

after_name: str
after: Optional[S]
_relative_path: PurePath
_output_path: PurePath

def __init__(
Expand All @@ -335,6 +352,7 @@ def __init__(
before: Optional[S],
after_name: str,
after: Optional[S],
relative_path: PurePath,
output_path: PurePath,
) -> None:
self.before_name = before_name
Expand All @@ -343,6 +361,7 @@ def __init__(
self.after_name = after_name
self.after = after

self._relative_path = relative_path
self._output_path = output_path

@property
Expand All @@ -357,7 +376,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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This highlights an interesting problem with docc... relative_path is supposed to point to an on-disk file, but we also use it to compute the display name.


@property
def output_path(self) -> PurePath:
Expand Down
83 changes: 83 additions & 0 deletions tests/spec_tools/test_docc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Regression tests for diff listing labels in docc-generated pages."""

from pathlib import PurePath

from docc.context import Context
from docc.plugins import 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 leaf label."""
listing = ListingSource(
PurePath("diffs/frontier/homestead/vm"),
PurePath("diffs/frontier/homestead/vm/index"),
{source},
)

context = Context({Source: listing})
root = html.HTMLRoot(context)
render_html(context, root, ListingNode({source}))

for child in root.children:
if isinstance(child, html.HTMLTag):
text = "".join(child._to_element().itertext()).strip()
return PurePath(text).name

raise AssertionError("listing render produced no HTML output")


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[Source] = DiffSource(
"frontier",
None,
"homestead",
None,
PurePath("diffs/frontier/homestead/vm/index"),
PurePath("diffs/frontier/homestead/vm/index"),
)
diff_source: DiffSource[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:
"""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[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
6 changes: 6 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading