diff --git a/docs/_diagrams/context_handshake.svg b/docs/_diagrams/context_handshake.svg index 04c7e988..5eb104fe 100644 --- a/docs/_diagrams/context_handshake.svg +++ b/docs/_diagrams/context_handshake.svg @@ -1,17 +1,17 @@ - + .d2-1485976774 .fill-N1{fill:#0A0F25;} + .d2-1485976774 .fill-N2{fill:#676C7E;} + .d2-1485976774 .fill-N3{fill:#9499AB;} + .d2-1485976774 .fill-N4{fill:#CFD2DD;} + .d2-1485976774 .fill-N5{fill:#DEE1EB;} + .d2-1485976774 .fill-N6{fill:#EEF1F8;} + .d2-1485976774 .fill-N7{fill:#FFFFFF;} + .d2-1485976774 .fill-B1{fill:#0A0F25;} + .d2-1485976774 .fill-B2{fill:#676C7E;} + .d2-1485976774 .fill-B3{fill:#9499AB;} + .d2-1485976774 .fill-B4{fill:#CFD2DD;} + .d2-1485976774 .fill-B5{fill:#DEE1EB;} + .d2-1485976774 .fill-B6{fill:#EEF1F8;} + .d2-1485976774 .fill-AA2{fill:#676C7E;} + .d2-1485976774 .fill-AA4{fill:#CFD2DD;} + .d2-1485976774 .fill-AA5{fill:#DEE1EB;} + .d2-1485976774 .fill-AB4{fill:#CFD2DD;} + .d2-1485976774 .fill-AB5{fill:#DEE1EB;} + .d2-1485976774 .stroke-N1{stroke:#0A0F25;} + .d2-1485976774 .stroke-N2{stroke:#676C7E;} + .d2-1485976774 .stroke-N3{stroke:#9499AB;} + .d2-1485976774 .stroke-N4{stroke:#CFD2DD;} + .d2-1485976774 .stroke-N5{stroke:#DEE1EB;} + .d2-1485976774 .stroke-N6{stroke:#EEF1F8;} + .d2-1485976774 .stroke-N7{stroke:#FFFFFF;} + .d2-1485976774 .stroke-B1{stroke:#0A0F25;} + .d2-1485976774 .stroke-B2{stroke:#676C7E;} + .d2-1485976774 .stroke-B3{stroke:#9499AB;} + .d2-1485976774 .stroke-B4{stroke:#CFD2DD;} + .d2-1485976774 .stroke-B5{stroke:#DEE1EB;} + .d2-1485976774 .stroke-B6{stroke:#EEF1F8;} + .d2-1485976774 .stroke-AA2{stroke:#676C7E;} + .d2-1485976774 .stroke-AA4{stroke:#CFD2DD;} + .d2-1485976774 .stroke-AA5{stroke:#DEE1EB;} + .d2-1485976774 .stroke-AB4{stroke:#CFD2DD;} + .d2-1485976774 .stroke-AB5{stroke:#DEE1EB;} + .d2-1485976774 .background-color-N1{background-color:#0A0F25;} + .d2-1485976774 .background-color-N2{background-color:#676C7E;} + .d2-1485976774 .background-color-N3{background-color:#9499AB;} + .d2-1485976774 .background-color-N4{background-color:#CFD2DD;} + .d2-1485976774 .background-color-N5{background-color:#DEE1EB;} + .d2-1485976774 .background-color-N6{background-color:#EEF1F8;} + .d2-1485976774 .background-color-N7{background-color:#FFFFFF;} + .d2-1485976774 .background-color-B1{background-color:#0A0F25;} + .d2-1485976774 .background-color-B2{background-color:#676C7E;} + .d2-1485976774 .background-color-B3{background-color:#9499AB;} + .d2-1485976774 .background-color-B4{background-color:#CFD2DD;} + .d2-1485976774 .background-color-B5{background-color:#DEE1EB;} + .d2-1485976774 .background-color-B6{background-color:#EEF1F8;} + .d2-1485976774 .background-color-AA2{background-color:#676C7E;} + .d2-1485976774 .background-color-AA4{background-color:#CFD2DD;} + .d2-1485976774 .background-color-AA5{background-color:#DEE1EB;} + .d2-1485976774 .background-color-AB4{background-color:#CFD2DD;} + .d2-1485976774 .background-color-AB5{background-color:#DEE1EB;} + .d2-1485976774 .color-N1{color:#0A0F25;} + .d2-1485976774 .color-N2{color:#676C7E;} + .d2-1485976774 .color-N3{color:#9499AB;} + .d2-1485976774 .color-N4{color:#CFD2DD;} + .d2-1485976774 .color-N5{color:#DEE1EB;} + .d2-1485976774 .color-N6{color:#EEF1F8;} + .d2-1485976774 .color-N7{color:#FFFFFF;} + .d2-1485976774 .color-B1{color:#0A0F25;} + .d2-1485976774 .color-B2{color:#676C7E;} + .d2-1485976774 .color-B3{color:#9499AB;} + .d2-1485976774 .color-B4{color:#CFD2DD;} + .d2-1485976774 .color-B5{color:#DEE1EB;} + .d2-1485976774 .color-B6{color:#EEF1F8;} + .d2-1485976774 .color-AA2{color:#676C7E;} + .d2-1485976774 .color-AA4{color:#CFD2DD;} + .d2-1485976774 .color-AA5{color:#DEE1EB;} + .d2-1485976774 .color-AB4{color:#CFD2DD;} + .d2-1485976774 .color-AB5{color:#DEE1EB;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0A0F25;--color-border-muted:#676C7E;--color-neutral-muted:#EEF1F8;--color-accent-fg:#676C7E;--color-accent-emphasis:#676C7E;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-1485976774);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-dark-d2-1485976774);mix-blend-mode:overlay}.sketch-overlay-B3{fill:url(#streaks-normal-d2-1485976774);mix-blend-mode:color-burn}.sketch-overlay-B4{fill:url(#streaks-normal-d2-1485976774);mix-blend-mode:color-burn}.sketch-overlay-B5{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-1485976774);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-normal-d2-1485976774);mix-blend-mode:color-burn}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-normal-d2-1485976774);mix-blend-mode:color-burn}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-1485976774);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-1485976774);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-1485976774);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-1485976774);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-1485976774);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]> - + - + - + -parent task(Portal)child actor task(@tractor.context fn) Start: open_context(fn, **kwargs) StartAck Started: await ctx.started(value) Yield: stream.send() Yield: stream.send() Stop: stream closed Return: fn return value - - - - - - - - +parent task(Portal)child actor task(@tractor.context fn)... or, instead of a graceful Returnparent-initiatedchild-initiated Start: open_context(fn, **kwargs) StartAck Started: await ctx.started(value) Yield: stream.send() Yield: stream.send() Stop: stream closed Return: fn return value ctx.cancel() | parent-side error Error: ContextCancelled Error: child raised | ContextCancelled + + + + + + + + + + + diff --git a/docs/_ext/d2diagrams.py b/docs/_ext/d2diagrams.py index c57f9028..8ac5545f 100644 --- a/docs/_ext/d2diagrams.py +++ b/docs/_ext/d2diagrams.py @@ -46,8 +46,10 @@ Usage, from __future__ import annotations import enum +import hashlib import os from pathlib import Path +import re import shlex import shutil import subprocess as sp @@ -273,6 +275,17 @@ class D2Diagram(SphinxDirective): ) if width := self.options.get('width'): img['width'] = width + # content-hash the rendered svg so the served + # `` carries a `?v=` cache-buster + # (sphinx tags css/js this way but not images); + # without it an edited diagram keeps showing the + # browser's stale copy at the stable + # `_images/.svg` url on autobuild reload. + if out.exists(): + img['d2_stem'] = out.stem + img['d2_cachebust'] = hashlib.sha256( + out.read_bytes() + ).hexdigest()[:8] fig = nodes.figure() fig += img classes: list[str] = ( @@ -318,11 +331,50 @@ def _reset_seen( _seen_outputs.clear() +def _cachebust_d2_images( + app: Sphinx, + pagename: str, + templatename: str, + context: dict, + doctree, +) -> None: + ''' + Append a ``?v=`` query to every d2 + diagram ```` in the rendered page ``body`` + so a browser re-fetches an *edited* SVG instead of + its stale cached copy at the stable + ``_images/.svg`` url (sphinx cache-busts + css/js this way, but not images). + + Scoped strictly to d2 images (those carrying the + `d2_cachebust` attr set in `D2Diagram.run()`); all + other markup is left untouched. + + ''' + if doctree is None or 'body' not in context: + return + busts: dict[str, str] = {} + for img in doctree.findall(nodes.image): + if ver := img.get('d2_cachebust'): + busts[img['d2_stem']] = ver + if not busts: + return + body: str = context['body'] + for stem, ver in busts.items(): + body = re.sub( + r'(src="[^"]*?' + re.escape(stem) + r'\.svg)(")', + rf'\1?v={ver}\2', + body, + ) + context['body'] = body + + def setup(app: Sphinx) -> dict: app.add_config_value('d2_bin', None, 'env') app.add_config_value('d2_args', [], 'env') app.add_directive('d2', D2Diagram) app.connect('builder-inited', _reset_seen) + app.connect('html-page-context', _cachebust_d2_images) return { 'version': '0.1.0', # NB: run() writes the rendered SVG during the diff --git a/docs/diagrams/context_handshake.d2 b/docs/diagrams/context_handshake.d2 index 11971825..2020d36f 100644 --- a/docs/diagrams/context_handshake.d2 +++ b/docs/diagrams/context_handshake.d2 @@ -19,3 +19,15 @@ parent -> child: "Yield: stream.send()" child -> parent: "Yield: stream.send()" parent -> child: "Stop: stream closed" child -> parent: "Return: fn return value" +# the non-graceful endings: errors/cancels always +# propagate, both ways (`ContextCancelled` rides in +# an `Error` msg, hence the `Error: ...` labels). +"... or, instead of a graceful Return": { + parent-initiated: { + parent -> child: "ctx.cancel() | parent-side error" + child -> parent: "Error: ContextCancelled" + } + child-initiated: { + child -> parent: "Error: child raised | ContextCancelled" + } +}