app, brt, rt = get_app()#show_logger=True, summary=True)DetailsJSON
Collapsible JSON/dict viewer component for notebooks
Derivation
<style>
me ul { list-style-type:none; list-style-position: outside; padding-inline-start: 22px; margin: 0; }
</style>
<details open>
<summary>Apollo astronauts</summary>
<ul>
<li><span>1</span>: Neil Armstrong</li>
<li><span>2</span>: Alan Bean</li>
<li><details>
<summary>Apollo 11</summary>
<ul>
<li><span>1</span>: Neil Armstrong</li>
<li><span>2</span>: Alan Bean</li>
<li><div><span>3</span>: Buzz Aldrin</div></li>
<li><span>4</span>: Edgar Mitchell</li>
<li><span>5</span>: Alan Shepard</li>
</ul></li>
<li><span>4</span>: Edgar Mitchell</li>
<li><span>5</span>: Alan Shepard</li>
</ul>
</details>Apollo astronauts
- 1: Neil Armstrong
- 2: Alan Bean
Apollo 11
- 1: Neil Armstrong
- 2: Alan Bean
- 3: Buzz Aldrin
- 4: Edgar Mitchell
- 5: Alan Shepard
- 4: Edgar Mitchell
- 5: Alan Shepard
def Val(v):
c = (
'null' if v is None else
'true' if v is True else
'false' if v is False else
'string' if isinstance(v, str) else
'number' if isinstance(v, (int, float)) else
'')
return Span(shorten(v, 'r', 140) if v is not None else 'None', cls=f"v {c}")
def NameVal(k, v): return Span(Span(k, cls='n'), ': ', Val(v))
class DetailsJSON(dict):
def __init__(self, *args, summary:str='', open:bool=True, openall:bool=False, skip:Sequence[str]=(), **kwargs):
super().__init__(*args, **kwargs)
self.summary, self.open, self.openall, self.skip = str(summary), open, openall, skip
def __ft__(self, d:Mapping|None=None, summary:str|None=None, lvl:int=0, open:bool=False):
if d is None: d = self; summary = self.summary or 'summary'; open=self.open
open = self.openall or open
return (
Style(self._css_) if lvl == 0 else (),
Details(open=open)(
Summary(summary, _n),
Ul()(*(
Li(NameVal(k, v)) if k in self.skip else
self.__ft__(v, k, lvl+1) if isinstance(v, Mapping) else
self.__ft__(dict(list(zip(range(len(v)), v))), k, lvl+1) if is_listy(v) else
Li(NameVal(k, v))
for k,v in d.items()))))
_css_ = (
'details ul { list-style-type:none; list-style-position: outside; padding-inline-start: 22px; margin: 0; } '
'''details .string { color: #24837b; } details .string::before { content: "'"; } details .string::after { content: "'"; } '''
'details .number { color: #ad8301; } '
'details .true { color: blue; } '
'details .false { color: red; } '
'details .null { color: gray; } '
'span.n { color: darkgrey; } '
)
# class DetailsJSON(dict):
# def __init__(self, *args, summary:str='', open:bool=False, lvl:int=0, **kwargs):
# super().__init__(*args, **kwargs)
# self.summary, self.open, self.lvl = summary, open, lvl
# def __ft__(self):
# return (
# Style(self._css_) if self.lvl == 0 else (),
# Details(open=self.open)(
# Summary(self.summary or 'summary', _n),
# Ul()(*(
# Li(NameVal(k, v)) if not isinstance(v, Mapping) else
# DetailsJSON(v, summary=k, lvl=self.lvl+1)
# for k,v in self.items()))))
# _css_ = 'me ul { list-style-type:none; list-style-position: outside; padding-inline-start: 22px; margin: 0; } '
dtl = DetailsJSON({
'1': 'Neil Armstrong',
'2': 'Alan Bean',
'3': 'Buzz Aldrin',
'letters': {
'a': 1,
'b': 2
},
'5': 'Edgar Mitchell',
'6': 'Alan Shepard'
}, summary='Apollo astronauts', open=True)
test_eq(val_at(dtl, '5'), 'Edgar Mitchell')
test_eq(val_at(dtl, 'letters.a'), 1)
print(to_xml(dtl))
show(dtl)<style>details ul { list-style-type:none; list-style-position: outside; padding-inline-start: 22px; margin: 0; } details .string { color: #24837b; } details .string::before { content: "'"; } details .string::after { content: "'"; } details .number { color: #ad8301; } details .true { color: blue; } details .false { color: red; } details .null { color: gray; } span.n { color: darkgrey; } </style>
<details open><summary>Apollo astronauts
</summary> <ul>
<li>
<span><span class="n">1</span>: <span class="v string">Neil Armstrong</span></span> </li>
<li>
<span><span class="n">2</span>: <span class="v string">Alan Bean</span></span> </li>
<li>
<span><span class="n">3</span>: <span class="v string">Buzz Aldrin</span></span> </li>
<details><summary>letters
</summary> <ul>
<li>
<span><span class="n">a</span>: <span class="v number">1</span></span> </li>
<li>
<span><span class="n">b</span>: <span class="v number">2</span></span> </li>
</ul>
</details> <li>
<span><span class="n">5</span>: <span class="v string">Edgar Mitchell</span></span> </li>
<li>
<span><span class="n">6</span>: <span class="v string">Alan Shepard</span></span> </li>
</ul>
</details>
Apollo astronauts
- 1: Neil Armstrong
- 2: Alan Bean
- 3: Buzz Aldrin
- a: 1
- b: 2
- 5: Edgar Mitchell
- 6: Alan Shepard
letters
def walk(m:Mapping, p:str=''):
for k,v in m.items():
if isinstance(v, Mapping): yield from walk(v, f"{p}.{k}" if p else k)
else: yield f"{p}.{k}" if p else k,v
list(walk(dtl))[('1', 'Neil Armstrong'),
('2', 'Alan Bean'),
('3', 'Buzz Aldrin'),
('letters.a', 1),
('letters.b', 2),
('5', 'Edgar Mitchell'),
('6', 'Alan Shepard')]
def walk(mapping: Mapping, prefix: str=''):
stack = [(prefix, list(mapping.items()))]
while stack:
p, items = stack[-1]
if not items: stack.pop(); continue
k, v = items.pop(0)
if isinstance(v, Mapping): stack.append((f"{p}.{k}" if p else k, list(v.items())))
else: yield f"{p}.{k}" if p else k, v
# Test
list(walk(dtl))[('1', 'Neil Armstrong'),
('2', 'Alan Bean'),
('3', 'Buzz Aldrin'),
('letters.a', 1),
('letters.b', 2),
('5', 'Edgar Mitchell'),
('6', 'Alan Shepard')]
def update(d, other:Mapping|None=None, **kwargs):
if isinstance(d, dict): d.update(other or {}, **kwargs)
else:
for k,v in {**(other or {}), **kwargs}.items():
try: setattr(d, k, v)
except AttributeError: pass
return dd = {'a': 1, 'b':2}
test_eq(update(d, c=3), {'a': 1, 'b':2, 'c':3})
test_eq(update(d, {'b': 22}), {'a': 1, 'b':22, 'c':3})DetailsJSON
DetailsJSON
DetailsJSON (o:Mapping[str,Any], summary:str='', open:Union[bool,Literal['all']]=True)
Base class for objects that provide routes via an APIRouter
NameVal
NameVal (k, v)
Val
Val (v)
brt.app.routes.clear()
bridge_cfg.auto_show = False
dtl = DetailsJSON({
'1': 'Neil Armstrong', '2': 'Alan Bean', '3': 'Buzz Aldrin',
'letters': { 'a': 1, 'b': True, 'c': {'d': None} },
'5': 'Edgar Mitchell', '6': 'Alan Shepard'
}, summary='Apollo astronauts')
brt.mount(dtl, '/details', 'details', show=False)
test_eq(dtl.ar.name(), 'DetailsJSON:details')
test_eq(app.url_path_for('DetailsJSON:details:get', dp=''), '/DetailsJSON/details/')
test_eq(app.url_path_for('DetailsJSON:details:get', dp='letters'), '/DetailsJSON/details/letters')
dtl()<details open hx-swap="outerHTML">
<style>me ul { list-style-type:none; list-style-position: outside; padding-inline-start: 22px; margin: 0; } me .string { color: #24837b; } me .string::before { content: "'"; } me .string::after { content: "'"; } me .number { color: #ad8301; } me .true { color: blue; } me .false { color: red; } me .null { color: gray; } me .n { color: darkgrey; } </style>
<summary>Apollo astronauts</summary>
<ul>
<li>
<span><span class="n">1</span>: <span class="v string">Neil Armstrong</span></span> </li>
<li>
<span><span class="n">2</span>: <span class="v string">Alan Bean</span></span> </li>
<li>
<span><span class="n">3</span>: <span class="v string">Buzz Aldrin</span></span> </li>
<li>
<details hx-get="/DetailsJSON/details/letters">
<summary>letters</summary>
</details> </li>
<li>
<span><span class="n">5</span>: <span class="v string">Edgar Mitchell</span></span> </li>
<li>
<span><span class="n">6</span>: <span class="v string">Alan Shepard</span></span> </li>
</ul>
</details>dtl('letters')<details open><summary>letters</summary>
<ul>
<li>
<span><span class="n">a</span>: <span class="v number">1</span></span> </li>
<li>
<span><span class="n">b</span>: <span class="v true">True</span></span> </li>
<li>
<details hx-get="/DetailsJSON/details/letters/c">
<summary>c</summary>
</details> </li>
</ul>
</details>dtl('letters/c')<details open><summary>c</summary>
<ul>
<li>
<span><span class="n">d</span>: <span class="v null">None</span></span> </li>
</ul>
</details>print(brt.cli.get('/DetailsJSON/details/letters', headers={'hx-request': '1'}).text)<details open><summary>letters</summary>
<ul>
<li>
<span><span class="n">a</span>: <span class="v number">1</span></span> </li>
<li>
<span><span class="n">b</span>: <span class="v true">True</span></span> </li>
<li>
<details hx-get="/DetailsJSON/details/letters/c">
<summary>c</summary>
</details> </li>
</ul>
</details>
print(brt.cli.get('/DetailsJSON/details/letters/c', headers={'hx-request': '1'}).text)<details open><summary>c</summary>
<ul>
<li>
<span><span class="n">d</span>: <span class="v null">None</span></span> </li>
</ul>
</details>
bridge_cfg.auto_show = True
dtl()Apollo astronauts
- 1: Neil Armstrong
- 2: Alan Bean
- 3: Buzz Aldrin
letters
- 5: Edgar Mitchell
- 6: Alan Shepard
brt('/DetailsJSON/details/');Apollo astronauts
- 1: Neil Armstrong
- 2: Alan Bean
- 3: Buzz Aldrin
-
letters
- 5: Edgar Mitchell
- 6: Alan Shepard
# bridge_cfg.auto_mount = True
apollo_astronauts = json.load(Path('static/apollo_astronauts.json').open())
(astro := DetailsJSON(apollo_astronauts, summary='Apollo astronauts'))Apollo astronauts
-
Apollo 7
-
Apollo 8
-
Apollo 9
-
Apollo 10
-
Apollo 11
-
Apollo 12
-
Apollo 13
-
Apollo 14
-
Apollo 15
-
Apollo 16
-
Apollo 17
request = {
'headers': {
'HX-Request': 'true',
'HX-Current-URL': 'vscode-webview://1ql27...enderer'
},
'headerNames': {
'hx-request': 'HX-Request',
'hx-current-url': 'HX-Current-URL'
},
'status': 0,
'method': 'GET',
'url': '/DetailsJSON_5096628128/Apollo 11/Buzz Aldrin',
'async': True,
'timeout': 0,
'withCredentials': False,
'body': None,
'req_id': '68ffadb0-958d-4346-b314-d9d62ca247d7'
}
response = {
'headers': {
'content-length': '441',
'content-type': 'text/html; charset=utf-8',
'last-modified': 'Fri, 15 Nov 2024 16:22:15 GMT',
'cache-control': 'no-store, no-cache, must-revalidate'
},
'status': 200,
'statusText': 'OK',
'data': '<details open><summary>Buzz Aldrin</summary>\n <ul>\n '
'<li>Pilot on Gemini 12 and Lunar Module pilot on Apollo 11.</li>\n '
'<li>Aldrin was the second person to walk on the moon.</li>\n <li>The maiden '
'name of Aldrin's mother was "Moon."</li>\n <li>While Neil was '
'the first human to step onto the moon, I'm the first alien from another '
'world to enter a spacecraft that was going to Earth.</li>\n '
'</ul>\n</details>',
'xml': None,
'finalUrl': 'http://nb/DetailsJSON_5096628128/Apollo%2011/Buzz%20Aldrin',
'req_id': '68ffadb0-958d-4346-b314-d9d62ca247d7'
}(req := DetailsJSON(request, summary='request', open='all'))request
-
headers
- HX-Request: true
- HX-Current-URL: vscode-webview://1ql27...enderer
-
headerNames
- hx-request: HX-Request
- hx-current-url: HX-Current-URL
- status: 0
- method: GET
- url: /DetailsJSON_5096628128/Apollo 11/Buzz Aldrin
- async: True
- timeout: 0
- withCredentials: False
- body: None
- req_id: 68ffadb0-958d-4346-b314-d9d62ca247d7
(resp := DetailsJSON(response, summary='response'))response
-
headers
- status: 200
- statusText: OK
- data: <details open><summary>Buzz Aldrin</summary> <ul> <li>Pilot on Gemini 12 and Lunar Module pilot on Apollo 11.</li> <li>Aldrin w…
- xml: None
- finalUrl: http://nb/DetailsJSON_5096628128/Apollo%2011/Buzz%20Aldrin
- req_id: 68ffadb0-958d-4346-b314-d9d62ca247d7