Skip to content

Commit cda4991

Browse files
committed
allow examples to be organized in directory
1 parent a2be8f1 commit cda4991

File tree

5 files changed

+78
-61
lines changed

5 files changed

+78
-61
lines changed

docs/examples.py

+29-10
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,28 @@
1515

1616

1717
def load_examples() -> Iterator[tuple[str, Callable[[], ComponentType]]]:
18-
for file in EXAMPLES_DIR.rglob("*.py"):
19-
yield get_example_name_from_file(file), load_one_example(file)
18+
for name in all_example_names():
19+
yield name, load_one_example(name)
2020

2121

2222
def all_example_names() -> set[str]:
23-
return {get_example_name_from_file(file) for file in EXAMPLES_DIR.rglob("*.py")}
23+
names = set()
24+
for file in EXAMPLES_DIR.rglob("*.py"):
25+
if file.name != "app.py" and (file.parent / "app.py").exists():
26+
# this isn't the main example file
27+
continue
28+
path = file.relative_to(EXAMPLES_DIR)
29+
if path.name == "app.py":
30+
path = path.parent
31+
else:
32+
path = path.with_suffix("")
33+
names.add(".".join(path.parts))
34+
return names
2435

2536

2637
def load_one_example(file_or_name: Path | str) -> Callable[[], ComponentType]:
2738
if isinstance(file_or_name, str):
28-
file = get_py_example_file_by_name(file_or_name)
39+
file = get_main_example_file_by_name(file_or_name)
2940
else:
3041
file = file_or_name
3142

@@ -66,16 +77,24 @@ def Wrapper():
6677
return Wrapper
6778

6879

69-
def get_example_name_from_file(file: Path) -> str:
70-
return ".".join(file.relative_to(EXAMPLES_DIR).with_suffix("").parts)
80+
def get_main_example_file_by_name(name: str) -> Path:
81+
path = _get_root_example_path_by_name(name)
82+
if path.is_dir():
83+
return path / "app.py"
84+
else:
85+
return path.with_suffix(".py")
7186

7287

73-
def get_py_example_file_by_name(name: str) -> Path:
74-
return EXAMPLES_DIR.joinpath(*name.split(".")).with_suffix(".py")
88+
def get_example_files_by_name(name: str) -> list[Path]:
89+
path = _get_root_example_path_by_name(name)
90+
if path.is_dir():
91+
return list(path.glob("*"))
92+
path = path.with_suffix(".py")
93+
return [path] if path.exists() else []
7594

7695

77-
def get_js_example_file_by_name(name: str) -> Path:
78-
return EXAMPLES_DIR.joinpath(*name.split(".")).with_suffix(".js")
96+
def _get_root_example_path_by_name(name: str) -> Path:
97+
return EXAMPLES_DIR.joinpath(*name.split("."))
7998

8099

81100
def _printout_viewer():

docs/source/_examples/super_simple_chart.py renamed to docs/source/_examples/super_simple_chart/app.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
from idom import component, run, web
44

55

6-
file = Path(__file__).parent / "super_simple_chart.js"
6+
file = Path(__file__).parent / "super-simple-chart.js"
77
ssc = web.module_from_file("super-simple-chart", file, fallback="⌛")
88
SuperSimpleChart = web.export(ssc, "SuperSimpleChart")
99

1010

1111
@component
1212
def App():
13-
print(__name__)
1413
return SuperSimpleChart(
1514
{
1615
"data": [

docs/source/_exts/widget_example.py

+47-48
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
from pathlib import Path
2+
from typing import Any
23

34
from docutils.parsers.rst import directives
45
from docutils.statemachine import StringList
56
from sphinx.application import Sphinx
67
from sphinx.util.docutils import SphinxDirective
78
from sphinx_design.tabs import TabSetDirective
89

9-
from docs.examples import get_js_example_file_by_name, get_py_example_file_by_name
10-
11-
12-
HERE = Path(__file__)
13-
EXAMPLES_DIR = HERE.parent.parent / "_examples"
10+
from docs.examples import EXAMPLES_DIR, get_example_files_by_name
1411

1512

1613
class WidgetExample(SphinxDirective):
@@ -31,54 +28,54 @@ def run(self):
3128
live_example_is_default_tab = "result-is-default-tab" in self.options
3229
activate_result = "activate-result" in self.options
3330

34-
py_ex_path = get_py_example_file_by_name(example_name)
35-
if not py_ex_path.exists():
31+
ex_files = get_example_files_by_name(example_name)
32+
if not ex_files:
3633
src_file, line_num = self.get_source_info()
3734
raise ValueError(
38-
f"Missing example file named {py_ex_path} referenced by document {src_file}:{line_num}"
35+
f"Missing example named {example_name!r} "
36+
f"referenced by document {src_file}:{line_num}"
3937
)
4038

41-
labeled_tab_items = {
42-
"python": _literal_include(
43-
name=str(py_ex_path.relative_to(EXAMPLES_DIR)),
44-
linenos=show_linenos,
45-
),
46-
"result": _interactive_widget(
39+
labeled_tab_items: list[tuple[str, Any]] = []
40+
if len(ex_files) == 1:
41+
labeled_tab_items.append(
42+
(
43+
"app.py",
44+
_literal_include(
45+
path=ex_files[0],
46+
linenos=show_linenos,
47+
),
48+
)
49+
)
50+
else:
51+
for path in sorted(ex_files, key=lambda p: p.name):
52+
labeled_tab_items.append(
53+
(
54+
path.name,
55+
_literal_include(
56+
path=path,
57+
linenos=show_linenos,
58+
),
59+
)
60+
)
61+
62+
result_tab_item = (
63+
"▶️ result",
64+
_interactive_widget(
4765
name=example_name,
4866
with_activate_button=not activate_result,
4967
),
50-
}
51-
52-
labeled_tab_titles = {
53-
"python": "Python",
54-
"javascript": "Javascript",
55-
"result": "▶️ Result",
56-
}
57-
58-
js_ex_path = get_js_example_file_by_name(example_name)
59-
if js_ex_path.exists():
60-
labeled_tab_items["javascript"] = _literal_include(
61-
name=str(js_ex_path.relative_to(EXAMPLES_DIR)),
62-
linenos=show_linenos,
63-
)
64-
65-
tab_label_order = (
66-
["result", "python", "javascript"]
67-
if live_example_is_default_tab
68-
else ["python", "javascript", "result"]
6968
)
69+
if live_example_is_default_tab:
70+
labeled_tab_items.insert(0, result_tab_item)
71+
else:
72+
labeled_tab_items.append(result_tab_item)
7073

7174
return TabSetDirective(
7275
"WidgetExample",
7376
[],
7477
{},
75-
_make_tab_items(
76-
[
77-
(labeled_tab_titles[label], labeled_tab_items[label])
78-
for label in tab_label_order
79-
if label in labeled_tab_items
80-
]
81-
),
78+
_make_tab_items(labeled_tab_items),
8279
self.lineno - 1,
8380
self.content_offset,
8481
"",
@@ -97,16 +94,18 @@ def _make_tab_items(labeled_content_tuples):
9794
return _string_to_nested_lines(tab_items)
9895

9996

100-
def _literal_include(name, linenos):
101-
if name.endswith(".py"):
102-
language = "python"
103-
elif name.endswith(".js"):
104-
language = "javascript"
105-
else:
106-
raise ValueError("Unknown extension type")
97+
def _literal_include(path: Path, linenos: bool):
98+
try:
99+
language = {
100+
".py": "python",
101+
".js": "javascript",
102+
".json": "json",
103+
}[path.suffix]
104+
except KeyError:
105+
raise ValueError(f"Unknown extension type {path.suffix!r}")
107106

108107
return _literal_include_template.format(
109-
name=name,
108+
name=str(path.relative_to(EXAMPLES_DIR)),
110109
language=language,
111110
linenos=":linenos:" if linenos else "",
112111
)

docs/source/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ To get a rough idea of how to write apps in IDOM, take a look at the tiny `"hell
5555

5656
.. note::
5757

58-
Try clicking the **▶️ Result** tab to see what this displays!
58+
Try clicking the **▶️ result** tab to see what this displays!
5959

6060
So what exactly does this code do? First, it imports a few tools from ``idom`` that will
6161
get used to describe and execute an application. Then, we create an ``App`` function

0 commit comments

Comments
 (0)