Skip to content

Commit b5618d1

Browse files
committed
docs and repr upgrades
1 parent 0002813 commit b5618d1

File tree

8 files changed

+174
-143
lines changed

8 files changed

+174
-143
lines changed

docs/_quarto.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ quartodoc:
7777
contents:
7878
- write_email_message_to_file
7979

80+
- title: Templated Authoring
81+
desc: >
82+
Write responsive emails with the Blastula template
83+
package: nbmail
84+
contents:
85+
- compose.compose_email
86+
- compose.create_blocks
87+
- compose.block_text
88+
- compose.block_title
89+
- compose.block_spacer
90+
- compose.block_image
91+
- compose.block_plot
92+
- compose.md
93+
- compose.add_cta_button
94+
- compose.add_readable_time
95+
8096
- title: MJML Authoring
8197
desc: >
8298
Write responsive emails with MJML

nbmail/compose/blocks.py

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .inline_utils import _is_url, _process_markdown
66

77

8+
89
__all__ = [
910
"Block",
1011
"BlockList",
@@ -48,6 +49,22 @@ def _to_mjml(self) -> MJMLTag:
4849
"""
4950
return self._mjml_tag
5051

52+
def _repr_html_(self) -> str:
53+
"""
54+
Return HTML representation for rich display in Jupyter notebooks.
55+
56+
Examples
57+
--------
58+
```{python}
59+
from nbmail.compose import block_text
60+
61+
block = block_text("Hello world!")
62+
block
63+
```
64+
"""
65+
block_list = BlockList(self)
66+
return block_list._repr_html_()
67+
5168

5269
class BlockList:
5370
"""
@@ -62,7 +79,7 @@ class BlockList:
6279
--------
6380
Users typically create BlockList via the `create_blocks()` function:
6481
65-
```python
82+
```{python}
6683
from nbmail.compose import create_blocks, block_text, block_title
6784
6885
content = create_blocks(
@@ -80,6 +97,36 @@ def __init__(self, *args: Union["Block", str]):
8097
One or more `Block` objects or strings.
8198
"""
8299
self.sections = list(args)
100+
101+
def _repr_html_(self) -> str:
102+
"""
103+
Return HTML representation for rich display in Jupyter notebooks.
104+
105+
This method wraps the BlockList in a compose_email() call and delegates
106+
to the Email's _repr_html_() method for rendering, enabling interactive
107+
preview of blocks directly in notebooks.
108+
109+
Returns
110+
-------
111+
str
112+
HTML content with inline attachments embedded as base64 data URIs.
113+
114+
Examples
115+
--------
116+
```{python}
117+
from nbmail.compose import create_blocks, block_title, block_text
118+
119+
content = create_blocks(
120+
block_title("My Email"),
121+
block_text("Hello world!")
122+
)
123+
content
124+
```
125+
"""
126+
from .compose import compose_email
127+
128+
email = compose_email(body=self)
129+
return email._repr_html_()
83130

84131
def _to_mjml_list(self) -> list[MJMLTag]:
85132
"""
@@ -134,12 +181,14 @@ def block_text(
134181
135182
Examples
136183
--------
137-
```python
184+
```{python}
138185
from nbmail.compose import block_text
139186
140187
# Simple text
141188
block = block_text("Hello world")
142189
190+
print(1+1)
191+
143192
# Markdown text
144193
block = block_text("This is **bold** and this is *italic*")
145194
@@ -179,14 +228,10 @@ def block_title(
179228
180229
Examples
181230
--------
182-
```python
231+
```{python}
183232
from nbmail.compose import block_title
184233
185-
# Simple title
186-
title = block_title("My Newsletter")
187-
188-
# Centered title (default)
189-
title = block_title("Welcome!", align="center")
234+
block_title("My Newsletter")
190235
```
191236
"""
192237

@@ -226,10 +271,10 @@ def block_spacer(height: str = "20px") -> Block:
226271
227272
Examples
228273
--------
229-
```python
274+
```{python}
230275
from nbmail.compose import block_spacer, create_blocks, block_text
231276
232-
email_body = create_blocks(
277+
create_blocks(
233278
block_text("First section"),
234279
block_spacer("30px"),
235280
block_text("Second section"),
@@ -292,14 +337,14 @@ def block_image(
292337
293338
Examples
294339
--------
295-
```python
296-
from nbmail.compose import block_image, create_blocks, block_text
340+
```{python}
341+
from nbmail.compose import block_image, create_blocks, block_text, compose_email, md
297342
298-
email = compose_email(
343+
compose_email(
344+
title="Product Feature",
299345
body=create_blocks(
300-
block_text("Here's an image:"),
301-
block_image("path/to/image.png", alt="Example", width=600),
302-
block_text("And some text after it.")
346+
block_text(md("Check this out:")),
347+
block_image("https://fastly.picsum.photos/id/630/300/200.jpg?hmac=dSM5_yM5Z9Pb3CX6OviVW3dEbyHmkD04otrIKU2LQ50", alt="Product image", width="500px")
303348
)
304349
)
305350
```
@@ -409,16 +454,17 @@ def block_plot(
409454
410455
Examples
411456
--------
412-
```python
413-
from nbmail.compose import block_plot, create_blocks, block_text
414-
from plotnine import ggplot, aes, geom_point, mtcars
457+
```{python}
458+
from nbmail.compose import block_plot, create_blocks, block_text, compose_email
459+
from plotnine import ggplot, aes, geom_point
460+
from great_tables.data import gtcars
415461
416462
plot = (
417-
ggplot(mtcars, aes("disp", "hp"))
463+
ggplot(gtcars, aes("trq", "hp"))
418464
+ geom_point()
419465
)
420466
421-
email = compose_email(
467+
compose_email(
422468
body=create_blocks(
423469
block_text("Here's my plot:"),
424470
block_plot(plot, alt="Scatter plot"),

nbmail/compose/compose.py

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ def create_blocks(*args: Union[Block, str]) -> BlockList:
3838
3939
Examples
4040
--------
41-
```python
41+
```{python}
4242
from nbmail.compose import create_blocks, block_text, block_title, block_spacer
4343
44-
content = create_blocks(
44+
create_blocks(
4545
block_title("Welcome!"),
4646
block_text("This is the main content."),
4747
block_spacer("20px"),
@@ -100,21 +100,21 @@ def compose_email(
100100
--------
101101
Simple email with single block:
102102
103-
```python
103+
```{python}
104104
from nbmail.compose import compose_email, block_text
105105
106-
email = compose_email(
106+
compose_email(
107107
body=block_text("This is a simple email.")
108108
)
109109
```
110110
111111
Email with title/header and multiple blocks:
112112
113-
```python
113+
```{python}
114114
from nbmail.compose import compose_email, create_blocks, block_title, block_text
115115
116-
email = compose_email(
117-
title="Welcome!", # Creates a large title block at top
116+
compose_email(
117+
title="Welcome!",
118118
body=create_blocks(
119119
block_text("Welcome to this week's update!"),
120120
block_text("Here's what's new...")
@@ -124,10 +124,10 @@ def compose_email(
124124
125125
Email with header section and body:
126126
127-
```python
127+
```{python}
128128
from nbmail.compose import compose_email, create_blocks, block_title, block_text
129129
130-
email = compose_email(
130+
compose_email(
131131
header=create_blocks(block_title("Newsletter")),
132132
body=create_blocks(
133133
block_text("Welcome to this week's update!"),
@@ -136,21 +136,6 @@ def compose_email(
136136
footer=create_blocks(block_text("© 2025 My Company"))
137137
)
138138
```
139-
140-
Email with embedded images:
141-
142-
```python
143-
from nbmail.compose import compose_email, block_image, block_text, md, create_blocks
144-
145-
# Use block_image for embedding local files or URLs
146-
email = compose_email(
147-
title="Product Feature",
148-
body=create_blocks(
149-
block_text(md("Check this out:")),
150-
block_image("path/to/image.png", alt="Product image", width="500px")
151-
)
152-
)
153-
```
154139
"""
155140
# Convert sections (header, body, footer) to MJML lists
156141
header_mjml_list = _component_to_mjml_section(header)

nbmail/compose/inline_utils.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,13 @@ def md(text: str) -> str:
6262
6363
Examples
6464
--------
65-
```python
66-
from nbmail.compose import md, block_text
65+
```{python}
66+
from nbmail.compose import md, block_text, compose_email
6767
68-
# Simple markdown
6968
html = md("This is **bold** and this is *italic*")
7069
7170
# Use in a block
72-
email = compose_email(body=block_text(html))
71+
compose_email(body=block_text(html))
7372
```
7473
"""
7574
return _process_markdown(text)
@@ -184,12 +183,12 @@ def add_cta_button(
184183
185184
Examples
186185
--------
187-
```python
186+
```{python}
188187
from nbmail.compose import add_cta_button, block_text, compose_email
189188
190189
button_html = add_cta_button("Learn More", "https://example.com")
191190
192-
email = compose_email(
191+
compose_email(
193192
body=block_text(f"Ready?\\n\\n{button_html}")
194193
)
195194
```
@@ -234,18 +233,13 @@ def add_readable_time(
234233
235234
Examples
236235
--------
237-
```python
236+
```{python}
238237
from datetime import datetime
239238
from nbmail.compose import add_readable_time, block_text, compose_email
240239
241240
time_str = add_readable_time(datetime.now())
242-
# Output: "November 10, 2025"
243241
244-
# Custom format
245-
time_str = add_readable_time(datetime.now(), format_str="%A, %B %d")
246-
# Output: "Sunday, November 10"
247-
248-
email = compose_email(
242+
compose_email(
249243
body=block_text(f"Report generated: {time_str}")
250244
)
251245
```

nbmail/egress.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -325,26 +325,6 @@ def send_email_with_smtp(
325325
email,
326326
security="tls"
327327
)
328-
329-
# SSL connection (port 465)
330-
send_email_with_smtp(
331-
"smtp.example.com",
332-
465,
333-
"user@example.com",
334-
"password123",
335-
email,
336-
security="ssl"
337-
)
338-
339-
# Plain SMTP (port 25) - insecure, for testing only
340-
send_email_with_smtp(
341-
"127.0.0.1",
342-
8025,
343-
"test@example.com",
344-
"password",
345-
email,
346-
security="smtp"
347-
)
348328
```
349329
"""
350330
if security not in ("tls", "ssl", "smtp"):

0 commit comments

Comments
 (0)