Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 0da9224

Browse files
authored
feat: support devsite notices (#222)
* feat: add support for various notices * fix: update docstrings and comments * test: update unit test
1 parent c350787 commit 0da9224

2 files changed

Lines changed: 116 additions & 1 deletion

File tree

docfx_yaml/extension.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,33 @@ class Bcolors:
101101
CODE = "code"
102102
PACKAGE = "package"
103103

104+
# DevSite specific notices that can be used.
105+
NOTE = 'note'
106+
CAUTION = 'caution'
107+
WARNING = 'warning'
108+
IMPORTANT = 'special'
109+
KEYPOINT = 'key-point'
110+
KEYTERM = 'key-term'
111+
OBJECTIVE = 'objective'
112+
SUCCESS = 'success'
113+
BETA = 'beta'
114+
PREVIEW = 'preview'
115+
DEPRECATED = 'deprecated'
116+
117+
NOTICES = {
118+
NOTE: 'Note',
119+
CAUTION: 'Caution',
120+
WARNING: 'Warning',
121+
IMPORTANT: 'Important',
122+
KEYPOINT: 'Key Point',
123+
KEYTERM: 'Key Term',
124+
OBJECTIVE: 'Objective',
125+
SUCCESS: 'Success',
126+
BETA: 'Beta',
127+
PREVIEW: 'Preview',
128+
DEPRECATED: 'deprecated',
129+
}
130+
104131
# Disable blib2to3 output that clutters debugging log.
105132
logging.getLogger("blib2to3").setLevel(logging.ERROR)
106133

@@ -407,6 +434,9 @@ def _parse_docstring_summary(summary):
407434
attribute_type_token = ":type:"
408435
keyword = name = description = var_type = ""
409436

437+
notice_open_tag = '<aside class="{notice_tag}">\n<b>{notice_name}:</b>'
438+
notice_close_tag = '</aside>'
439+
410440
# We need to separate in chunks, which is defined by 3 newline breaks.
411441
# Otherwise when parsing for code and blocks of stuff, we will not be able
412442
# to have the entire context when just splitting by single newlines.
@@ -468,6 +498,24 @@ def _parse_docstring_summary(summary):
468498

469499
continue
470500

501+
elif keyword and keyword in NOTICES:
502+
# Determine how much code block is indented to format properly.
503+
if tab_space == -1:
504+
parts = [split_part for split_part in part.split("\n") if split_part]
505+
tab_space = len(parts[0]) - len(parts[0].lstrip(" "))
506+
if tab_space == 0:
507+
raise ValueError("Content in the block should be indented."\
508+
f"Please check the docstring: \n{summary}")
509+
if not part.startswith(" "*tab_space):
510+
if notice_body:
511+
parts = [indent_code_left(part, tab_space) for part in notice_body]
512+
summary_parts.append("\n".join(parts))
513+
514+
summary_parts.append(notice_close_tag)
515+
keyword = ""
516+
notice_body.append(part)
517+
continue
518+
471519
# Parse keywords if found.
472520
# lstrip is added to parse code blocks that are not formatted well.
473521
if part.lstrip('\n').startswith('..'):
@@ -491,6 +539,24 @@ def _parse_docstring_summary(summary):
491539
found_name = False
492540
name = part.split("::")[1].strip()
493541

542+
# Extracts the notice content and format it.
543+
elif keyword and keyword in NOTICES:
544+
summary_parts.append(notice_open_tag.format(
545+
notice_tag=keyword, notice_name=NOTICES[keyword]))
546+
tab_space = -1
547+
notice_body = []
548+
parts = [split_part for split_part in part.split("\n") if split_part][1:]
549+
if not parts:
550+
continue
551+
tab_space = len(parts[0]) - len(parts[0].lstrip(" "))
552+
if tab_space == 0:
553+
raise ValueError("Content in the block should be indented."\
554+
f"Please check the docstring: \n{summary}")
555+
parts = [indent_code_left(part, tab_space) for part in parts]
556+
summary_parts.append("\n".join(parts))
557+
summary_parts.append(notice_close_tag)
558+
keyword = ""
559+
494560
# Reserve for additional parts
495561
# elif keyword == keyword:
496562
else:
@@ -508,6 +574,13 @@ def _parse_docstring_summary(summary):
508574
if summary_parts[-1] != "```\n":
509575
summary_parts.append("```\n")
510576

577+
if keyword and keyword in NOTICES:
578+
if notice_body:
579+
parts = [indent_code_left(part, tab_space) for part in notice_body]
580+
summary_parts.append("\n\n".join(parts))
581+
if summary_parts[-1] != notice_close_tag:
582+
summary_parts.append(notice_close_tag)
583+
511584
# Requires 2 newline chars to properly show on cloud site.
512585
return "\n".join(summary_parts), attributes
513586

tests/test_unit.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ def get_client_cert():
808808
# Check that nothing much changes otherwise.
809809
summary = \
810810
"""
811-
.. note::
811+
.. literalinclude::
812812
note that these are not supported yet, so they will be ignored for now.
813813
814814
And any other documentation that the source code would have could go here.
@@ -833,6 +833,48 @@ def get_client_cert():
833833
with self.assertRaises(ValueError):
834834
_parse_docstring_summary(summary)
835835

836+
# Check that notices are processed properly.
837+
summary_want = \
838+
"""<aside class="note">
839+
<b>Note:</b>
840+
this is a note.
841+
</aside>
842+
<aside class="caution">
843+
<b>Caution:</b>
844+
another type of notice.
845+
</aside>
846+
<aside class="key-term">
847+
<b>Key Term:</b>
848+
hyphenated term notice.
849+
</aside>"""
850+
851+
summary = \
852+
"""
853+
.. note::
854+
\n this is a note.
855+
856+
857+
.. caution::
858+
\n another type of notice.
859+
860+
861+
.. key-term::
862+
\n hyphenated term notice.
863+
"""
864+
865+
summary_got, attributes_got = _parse_docstring_summary(summary)
866+
self.assertEqual(summary_got, summary_want)
867+
self.assertEqual(attributes_got, attributes_want)
868+
869+
# Check that exception is raised if block is not formatted properly.
870+
871+
summary = \
872+
"""
873+
.. warning::
874+
this is not a properly formatted warning.
875+
"""
876+
with self.assertRaises(ValueError):
877+
_parse_docstring_summary(summary)
836878

837879
def test_parse_docstring_summary_attributes(self):
838880
# Test parsing docstring with attributes.

0 commit comments

Comments
 (0)