Skip to content

Commit cb29cc3

Browse files
committed
style: format code with ruff format
1 parent d209280 commit cb29cc3

4 files changed

Lines changed: 115 additions & 42 deletions

File tree

backend/app/gpg/service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ async def extract_and_import_keys_from_email(
189189
return imported
190190

191191

192-
193192
def _parse_uid(uid: str) -> tuple[str | None, str | None]:
194193
"""Parse a GnuPG UID string like ``Name <email>`` into ``(name, email)``."""
195194
if "<" in uid and ">" in uid:

backend/tests/test_gpg_auto_import.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ def test_send_email_request_accepts_bcc_addresses() -> None:
2626

2727

2828
@pytest.mark.asyncio
29-
async def test_extract_and_import_keys_from_email_extracts_armored_blocks(_engine_and_session, monkeypatch) -> None:
29+
async def test_extract_and_import_keys_from_email_extracts_armored_blocks(
30+
_engine_and_session, monkeypatch
31+
) -> None:
3032
"""extract_and_import_keys_from_email extracts PGP public key blocks from raw bytes and imports them."""
3133
_engine, factory = _engine_and_session
3234
sample_key = (
@@ -63,4 +65,3 @@ async def fake_import_key(req, db):
6365
)
6466
assert len(imported) == 1
6567
assert imported[0].mailbox_address == "sender@example.com"
66-

scripts/build_docs.py

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,61 @@
1010
GUIDES_DIR = os.path.join(DOCS_DIR, "guides")
1111

1212
GUIDES = [
13-
{"file": "architecture.md", "title": "Architecture", "desc": "Container layout, request flow, and tech stack."},
14-
{"file": "configuration.md", "title": "Configuration", "desc": "Environment variables and exposed ports."},
15-
{"file": "api.md", "title": "API Reference", "desc": "REST endpoints, authentication, and API key scopes."},
16-
{"file": "production.md", "title": "Production Deployment", "desc": "Hardened mode, DNS records, and TLS certificates."},
17-
{"file": "tunnel.md", "title": "SMTP Tunnel", "desc": "Bypass port-25 blocks on cloud providers with a secure, authenticated SMTP egress tunnel."},
18-
{"file": "clients.md", "title": "Email Clients & TLS Trust", "desc": "IMAP/POP3/SMTP setup and trusting the CA."},
19-
{"file": "ci.md", "title": "Using in CI/CD", "desc": "Pipeline setup and platform examples."},
20-
{"file": "mcp.md", "title": "MCP Server", "desc": "Give an AI agent its own mailbox over MCP."},
21-
{"file": "sandbox.md", "title": "Provider Sandbox & HTTP Bin", "desc": "Capture SMS, voice, and chat traffic, and inspect HTTP requests."},
22-
{"file": "networking.md", "title": "Sharing MailCue", "desc": "Run one container behind a shared Docker network."},
23-
{"file": "development.md", "title": "Development & Contributing", "desc": "Local setup, linting, tests, and the PR process."}
13+
{
14+
"file": "architecture.md",
15+
"title": "Architecture",
16+
"desc": "Container layout, request flow, and tech stack.",
17+
},
18+
{
19+
"file": "configuration.md",
20+
"title": "Configuration",
21+
"desc": "Environment variables and exposed ports.",
22+
},
23+
{
24+
"file": "api.md",
25+
"title": "API Reference",
26+
"desc": "REST endpoints, authentication, and API key scopes.",
27+
},
28+
{
29+
"file": "production.md",
30+
"title": "Production Deployment",
31+
"desc": "Hardened mode, DNS records, and TLS certificates.",
32+
},
33+
{
34+
"file": "tunnel.md",
35+
"title": "SMTP Tunnel",
36+
"desc": "Bypass port-25 blocks on cloud providers with a secure, authenticated SMTP egress tunnel.",
37+
},
38+
{
39+
"file": "clients.md",
40+
"title": "Email Clients & TLS Trust",
41+
"desc": "IMAP/POP3/SMTP setup and trusting the CA.",
42+
},
43+
{
44+
"file": "ci.md",
45+
"title": "Using in CI/CD",
46+
"desc": "Pipeline setup and platform examples.",
47+
},
48+
{
49+
"file": "mcp.md",
50+
"title": "MCP Server",
51+
"desc": "Give an AI agent its own mailbox over MCP.",
52+
},
53+
{
54+
"file": "sandbox.md",
55+
"title": "Provider Sandbox & HTTP Bin",
56+
"desc": "Capture SMS, voice, and chat traffic, and inspect HTTP requests.",
57+
},
58+
{
59+
"file": "networking.md",
60+
"title": "Sharing MailCue",
61+
"desc": "Run one container behind a shared Docker network.",
62+
},
63+
{
64+
"file": "development.md",
65+
"title": "Development & Contributing",
66+
"desc": "Local setup, linting, tests, and the PR process.",
67+
},
2468
]
2569

2670
# Generate Pygments syntax highlighting CSS
@@ -466,51 +510,63 @@
466510
</html>
467511
"""
468512

513+
469514
def generate_docs():
470515
print("Generating HTML documentation...")
471-
516+
472517
# Auto-copy and adapt tunnel README to docs/guides/tunnel.md
473518
tunnel_readme = os.path.join(WORKSPACE_ROOT, "tunnel", "README.md")
474519
tunnel_guide = os.path.join(GUIDES_DIR, "tunnel.md")
475520
if os.path.exists(tunnel_readme):
476521
with open(tunnel_readme, "r", encoding="utf-8") as f:
477522
content = f.read()
478523
# Fix relative links to tunnel/docs/PROTOCOL.md and SECURITY.md
479-
content = content.replace("](docs/PROTOCOL.md)", "](https://github.com/Olib-AI/mailcue/blob/main/tunnel/docs/PROTOCOL.md)")
480-
content = content.replace("](docs/SECURITY.md)", "](https://github.com/Olib-AI/mailcue/blob/main/tunnel/docs/SECURITY.md)")
524+
content = content.replace(
525+
"](docs/PROTOCOL.md)",
526+
"](https://github.com/Olib-AI/mailcue/blob/main/tunnel/docs/PROTOCOL.md)",
527+
)
528+
content = content.replace(
529+
"](docs/SECURITY.md)",
530+
"](https://github.com/Olib-AI/mailcue/blob/main/tunnel/docs/SECURITY.md)",
531+
)
481532
with open(tunnel_guide, "w", encoding="utf-8") as f:
482533
f.write(content)
483534
print("Copied and adapted tunnel/README.md to docs/guides/tunnel.md")
484-
535+
485536
# Render all markdown guides
486537
for guide in GUIDES:
487538
md_file_path = os.path.join(GUIDES_DIR, guide["file"])
488539
html_filename = guide["file"].replace(".md", ".html")
489540
html_file_path = os.path.join(GUIDES_DIR, html_filename)
490-
541+
491542
if not os.path.exists(md_file_path):
492543
print(f"Warning: {md_file_path} not found.")
493544
continue
494-
545+
495546
with open(md_file_path, "r", encoding="utf-8") as f:
496547
md_content = f.read()
497-
548+
498549
# Pre-process markdown to extract mermaid blocks before markdown parses them (and codehilite ruins them)
499550
mermaid_blocks = []
551+
500552
def extract_mermaid(match):
501553
block_content = match.group(1).strip()
502554
mermaid_blocks.append(block_content)
503-
return f"\n\n<!-- MERMAID_PLACEHOLDER_{len(mermaid_blocks)-1} -->\n\n"
504-
505-
md_content_no_mermaid = re.sub(r'```mermaid([\s\S]*?)```', extract_mermaid, md_content)
506-
555+
return f"\n\n<!-- MERMAID_PLACEHOLDER_{len(mermaid_blocks) - 1} -->\n\n"
556+
557+
md_content_no_mermaid = re.sub(
558+
r"```mermaid([\s\S]*?)```", extract_mermaid, md_content
559+
)
560+
507561
# Parse markdown to HTML
508562
# Using extensions:
509563
# extra: includes tables, footnotes, attribute lists, etc.
510564
# codehilite: syntax highlighting
511565
# fenced_code: code blocks
512-
html_body = markdown.markdown(md_content_no_mermaid, extensions=['extra', 'codehilite', 'fenced_code'])
513-
566+
html_body = markdown.markdown(
567+
md_content_no_mermaid, extensions=["extra", "codehilite", "fenced_code"]
568+
)
569+
514570
# Post-process HTML
515571
# 1. Map relative markdown links to HTML links
516572
# Match links to other guides (e.g. 'architecture.md' or '../guides/architecture.md')
@@ -525,9 +581,9 @@ def replace_link(match):
525581
if g["file"] == basename:
526582
return f'href="{basename.replace(".md", ".html")}"'
527583
return match.group(0)
528-
584+
529585
html_body = re.sub(r'href="([^"]+)"', replace_link, html_body)
530-
586+
531587
# 2. Put mermaid blocks back as clean <pre class="mermaid"> tags
532588
for i, block in enumerate(mermaid_blocks):
533589
placeholder = f"<!-- MERMAID_PLACEHOLDER_{i} -->"
@@ -537,7 +593,7 @@ def replace_link(match):
537593
html_body = html_body.replace(p_placeholder, mermaid_html)
538594
else:
539595
html_body = html_body.replace(placeholder, mermaid_html)
540-
596+
541597
# 3. Generate Sidebar Links dynamically
542598
sidebar_links = []
543599
for g in GUIDES:
@@ -547,26 +603,28 @@ def replace_link(match):
547603
f'<li><a href="{g_html}" class="sidebar-link {active_class}">{g["title"]}</a></li>'
548604
)
549605
sidebar_links_str = "\n ".join(sidebar_links)
550-
606+
551607
# Populate Page Template
552608
page_html = PAGE_TEMPLATE.format(
553609
title=guide["title"],
554610
desc=guide["desc"],
555611
html_filename=html_filename,
556612
sidebar_links=sidebar_links_str,
557613
content=html_body,
558-
pygments_css=pygments_css
614+
pygments_css=pygments_css,
559615
)
560-
616+
561617
with open(html_file_path, "w", encoding="utf-8") as f:
562618
f.write(page_html)
563-
619+
564620
print(f"Generated: docs/guides/{html_filename}")
565621

566622
# Generate robots.txt
567623
robots_path = os.path.join(DOCS_DIR, "robots.txt")
568624
with open(robots_path, "w", encoding="utf-8") as f:
569-
f.write("User-agent: *\nAllow: /\n\nSitemap: https://olib-ai.github.io/mailcue/sitemap.xml\n")
625+
f.write(
626+
"User-agent: *\nAllow: /\n\nSitemap: https://olib-ai.github.io/mailcue/sitemap.xml\n"
627+
)
570628
print("Generated: docs/robots.txt")
571629

572630
# Generate sitemap.xml
@@ -575,20 +633,30 @@ def replace_link(match):
575633
"https://olib-ai.github.io/mailcue/",
576634
]
577635
for g in GUIDES:
578-
sitemap_urls.append(f"https://olib-ai.github.io/mailcue/guides/{g['file'].replace('.md', '.html')}")
579-
580-
sitemap_xml = ['<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
636+
sitemap_urls.append(
637+
f"https://olib-ai.github.io/mailcue/guides/{g['file'].replace('.md', '.html')}"
638+
)
639+
640+
sitemap_xml = [
641+
'<?xml version="1.0" encoding="UTF-8"?>',
642+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
643+
]
581644
for url in sitemap_urls:
582645
sitemap_xml.append(" <url>")
583646
sitemap_xml.append(f" <loc>{url}</loc>")
584647
sitemap_xml.append(" <changefreq>weekly</changefreq>")
585-
sitemap_xml.append(" <priority>1.0</priority>" if url.endswith("/mailcue/") else " <priority>0.8</priority>")
648+
sitemap_xml.append(
649+
" <priority>1.0</priority>"
650+
if url.endswith("/mailcue/")
651+
else " <priority>0.8</priority>"
652+
)
586653
sitemap_xml.append(" </url>")
587654
sitemap_xml.append("</urlset>")
588-
655+
589656
with open(sitemap_path, "w", encoding="utf-8") as f:
590657
f.write("\n".join(sitemap_xml))
591658
print("Generated: docs/sitemap.xml")
592659

660+
593661
if __name__ == "__main__":
594662
generate_docs()

sdks/_e2e/python_e2e.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ async def step_async(name, coro):
171171
username = f"sdk-async-{uuid.uuid4().hex[:8]}"
172172
addr = f"{username}@mailcue.local"
173173
await step_async(
174-
"mailboxes.create", mc.mailboxes.create(username, "testpass123", domain="mailcue.local")
174+
"mailboxes.create",
175+
mc.mailboxes.create(username, "testpass123", domain="mailcue.local"),
175176
)
176177
await step_async(
177178
"emails.inject",
@@ -210,7 +211,11 @@ async def listen():
210211
nonlocal got_event, got_type
211212
async for ev in mc.events.stream():
212213
got_event = True
213-
got_type = getattr(ev, "type", None) or getattr(ev, "event_type", None) or repr(ev)
214+
got_type = (
215+
getattr(ev, "type", None)
216+
or getattr(ev, "event_type", None)
217+
or repr(ev)
218+
)
214219
break
215220

216221
with contextlib.suppress(asyncio.TimeoutError):

0 commit comments

Comments
 (0)