Skip to content

Commit

Permalink
Support rendering JSON in a Jupyter Notebook
Browse files Browse the repository at this point in the history
Signed-off-by: Jinzhe Zeng <[email protected]>
  • Loading branch information
njzjz committed Feb 5, 2024
1 parent ce0bc39 commit 8c1eeca
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 2 deletions.
217 changes: 217 additions & 0 deletions dargs/notebook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
from typing import List, Union
from IPython.core.display import display, HTML

Check warning on line 2 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L1-L2

Added lines #L1 - L2 were not covered by tests

from dargs import Argument, Variant
import json
import re

Check warning on line 6 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L4-L6

Added lines #L4 - L6 were not covered by tests

__all__ = ["JSON"]

Check warning on line 8 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L8

Added line #L8 was not covered by tests

# https://www.w3schools.com/css/css_tooltip.asp
css = """<style>

Check warning on line 11 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L11

Added line #L11 was not covered by tests
.dargs-codeblock {
width: 100%;
background-color: #f9f9f9;
border-color: #f9f9f9;
color: #000000;
}
.dargs-codeblock::before {
counter-reset: listing;
}
.dargs-codeblock code.dargs-linebegin {
counter-increment: listing;
}
.dargs-codeblock code.dargs-linebegin::before {
content: counter(listing) " ";
display: inline-block;
width: 2em;
padding-left: auto;
margin-left: auto;
text-align: right;
color: #6e7781;
}
.dargs-codeblock code.dargs-code {
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0;
background-color: #f9f9f9;
border-color: #f9f9f9;
color: #000000;
}
.dargs-codeblock .dargs-key {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}
.dargs-codeblock .dargs-key code.dargs-code {
color: #0550ae;
}
.dargs-codeblock .dargs-key .dargs-doc {
visibility: hidden;
width: 600px;
background-color: black;
color: #fff;
padding: 1em 1em;
border-radius: 6px;
position: absolute;
z-index: 1;
}
.dargs-codeblock .dargs-key:hover .dargs-doc {
visibility: visible;
}
.dargs-codeblock .dargs-key .dargs-doc .dargs-doc-code {
color: #bbbbff;
}
</style>
"""


def JSON(data: Union[dict, str], arg: Union[Argument, List[Argument]]):

Check warning on line 70 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L70

Added line #L70 was not covered by tests
"""Display JSON data with Argument in the Jupyter Notebook.
Parameters
----------
data : dict or str
The JSON data to be displayed, either JSON string or a dict.
arg : dargs.Argument or list[dargs.Argument]
The Argument that describes the JSON data.
"""
if isinstance(data, str):
data = json.loads(data)
elif isinstance(data, dict):
pass

Check warning on line 83 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L80-L83

Added lines #L80 - L83 were not covered by tests
else:
raise ValueError(f"Unknown type: {type(data)}")

Check warning on line 85 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L85

Added line #L85 was not covered by tests

if isinstance(arg, list):
arg = Argument("data", dtype=dict, sub_fields=arg)
elif isinstance(arg, Argument):
pass

Check warning on line 90 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L87-L90

Added lines #L87 - L90 were not covered by tests
else:
raise ValueError(f"Unknown type: {type(arg)}")
argdata = ArgumentData(data, arg)
buff = [css, r"""<div class="dargs-codeblock">""", argdata.print_html(), "</div>"]

Check warning on line 94 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L92-L94

Added lines #L92 - L94 were not covered by tests

display(HTML("".join(buff)))

Check warning on line 96 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L96

Added line #L96 was not covered by tests


class ArgumentData:
def __init__(self, data: dict, arg: Argument):
self.data = data
self.arg = arg
self.subdata = []
self._init_subdata()

Check warning on line 104 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L99-L104

Added lines #L99 - L104 were not covered by tests

def _init_subdata(self):

Check warning on line 106 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L106

Added line #L106 was not covered by tests
"""Initialize sub ArgumentData."""
if isinstance(self.data, dict):
sub_fields = self.arg.sub_fields.copy()

Check warning on line 109 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L108-L109

Added lines #L108 - L109 were not covered by tests
# extend subfiles with sub_variants
for vv in self.arg.sub_variants.values():
choice = self.data.get(vv.flag_name, vv.default_tag)
if choice and choice in vv.choice_dict:
sub_fields.update(vv.choice_dict[choice].sub_fields)

Check warning on line 114 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L111-L114

Added lines #L111 - L114 were not covered by tests

for kk in self.data:
if kk in sub_fields:
self.subdata.append(ArgumentData(self.data[kk], sub_fields[kk]))
elif kk in self.arg.sub_variants:
self.subdata.append(

Check warning on line 120 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L116-L120

Added lines #L116 - L120 were not covered by tests
ArgumentData(self.data[kk], self.arg.sub_variants[kk])
)
else:
self.subdata.append(ArgumentData(self.data[kk], kk))

Check warning on line 124 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L124

Added line #L124 was not covered by tests

def print_html(self, _level=0, _last_one=True):
linebreak = "<br/>"
indent = (

Check warning on line 128 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L126-L128

Added lines #L126 - L128 were not covered by tests
r"""<code class="dargs-code dargs-linebegin">"""
+ "&nbsp;" * (_level * 2)
+ "</code>"
)
buff = []
buff.append(indent)
if _level > 0:
if isinstance(self.arg, (Argument, Variant)):
buff.append(r"""<span class="dargs-key">""")

Check warning on line 137 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L133-L137

Added lines #L133 - L137 were not covered by tests
else:
buff.append(r"""<span>""")
buff.append(r"""<code class="dargs-code">""")
buff.append('"')
if isinstance(self.arg, Argument):
buff.append(self.arg.name)
elif isinstance(self.arg, Variant):
buff.append(self.arg.flag_name)
elif isinstance(self.arg, str):
buff.append(self.arg)

Check warning on line 147 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L139-L147

Added lines #L139 - L147 were not covered by tests
else:
raise ValueError(f"Unknown type: {type(self.arg)}")
buff.append('"')
buff.append("</code>")
if isinstance(self.arg, (Argument, Variant)):
buff.append(r"""<span class="dargs-doc">""")
if isinstance(self.arg, Argument):
doc_head = (

Check warning on line 155 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L149-L155

Added lines #L149 - L155 were not covered by tests
self.arg.gen_doc_head()
.replace("| type:", "type:")
.replace("\n", linebreak)
)
# use re to replace ``xx`` to <code>xx</code>
doc_head = re.sub(

Check warning on line 161 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L161

Added line #L161 was not covered by tests
r"``(.*?)``", r'<span class="dargs-doc-code">\1</span>', doc_head
)
doc_head = re.sub(r"\*(.+)\*", r"<i>\1</i>", doc_head)
buff.append(doc_head)
elif isinstance(self.arg, Variant):
buff.append(f"{self.arg.flag_name}:<br/>type: ")
buff.append(r"""<span class="dargs-doc-code">""")
buff.append("str")
buff.append(r"""</span>""")
if self.arg.default_tag:
buff.append(", default: ")
buff.append(r"""<span class="dargs-doc-code">""")
buff.append(self.arg.default_tag)
buff.append(r"""</span>""")

Check warning on line 175 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L164-L175

Added lines #L164 - L175 were not covered by tests
else:
raise ValueError(f"Unknown type: {type(self.arg)}")

Check warning on line 177 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L177

Added line #L177 was not covered by tests

buff.append(linebreak)
buff.append(linebreak)
doc_body = self.arg.doc.strip()
doc_body = re.sub(r"""\n+""", "\n", doc_body)
doc_body = doc_body.replace("\n", linebreak)
doc_body = re.sub(

Check warning on line 184 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L179-L184

Added lines #L179 - L184 were not covered by tests
r"`+(.*?)`+", r'<span class="dargs-doc-code">\1</span>', doc_body
)
doc_body = re.sub(r"\*(.+)\*", r"<i>\1</i>", doc_body)
buff.append(doc_body)

Check warning on line 188 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L187-L188

Added lines #L187 - L188 were not covered by tests

buff.append(r"""</span></span>""")
buff.append(r"""<code class="dargs-code">""")
buff.append(": ")
buff.append("</code>")
if self.subdata:
buff.append(r"""<code class="dargs-code">""")
buff.append("{")
buff.append("</code>")
buff.append(linebreak)
for ii, sub in enumerate(self.subdata):
buff.append(sub.print_html(_level + 1, _last_one=(ii == len(self.subdata) - 1)))
buff.append(indent)
buff.append(r"""<code class="dargs-code">""")
buff.append("}")
if not _last_one:
buff.append(",")
buff.append("</code>")
buff.append(linebreak)

Check warning on line 207 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L190-L207

Added lines #L190 - L207 were not covered by tests
else:
buff.append(r"""<code class="dargs-code">""")
buff.append(

Check warning on line 210 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L209-L210

Added lines #L209 - L210 were not covered by tests
json.dumps(self.data, indent=2).replace("\n", f"{linebreak}{indent}")
)
if not _last_one:
buff.append(",")
buff.append("</code>")
buff.append(linebreak)
return "".join(buff)

Check warning on line 217 in dargs/notebook.py

View check run for this annotation

Codecov / codecov/patch

dargs/notebook.py#L213-L217

Added lines #L213 - L217 were not covered by tests
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"sphinx.ext.viewcode",
"sphinx.ext.intersphinx",
"numpydoc",
"myst_parser",
"myst_nb",
"dargs.sphinx",
]

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Welcome to dargs's documentation!
intro
sphinx
dpgui
nb
api/api
credits

Expand Down
46 changes: 46 additions & 0 deletions docs/nb.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Use with Jupyter Notebook\n",
"\n",
"In a [Jupyter Notebook](https://jupyter.org/), with {meth}`dargs.notebook.JSON`, one can render an JSON string with an Argument."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dargs.sphinx import _test_argument\n",
"from dargs.notebook import JSON\n",
"\n",
"jstr = \"\"\"\n",
"{\n",
" \"test_argument\": \"test1\",\n",
" \"test_variant\": \"test_variant_argument\",\n",
" \"_comment\": \"This is an example data\"\n",
"}\n",
"\"\"\"\n",
"JSON(jstr, _test_argument())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When the monse stays on an argument key, the documentation of this argument will pop up."
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.
numpydoc
deepmodeling_sphinx>=0.1.1
myst_parser
myst-nb
sphinx_rtd_theme

0 comments on commit 8c1eeca

Please sign in to comment.