From ca2b9878e1d539e98cda134ff9e0963a87be3afa Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Mon, 1 May 2023 14:54:59 -0700 Subject: [PATCH 01/13] Converts admonition title elements from p tags to h2 tags --- docs/source/_static/css/custom.css | 29 +++++++++++++++++++++++++++++ docs/source/_static/js/custom.js | 25 ++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css index a559c5da5f..7497773a31 100644 --- a/docs/source/_static/css/custom.css +++ b/docs/source/_static/css/custom.css @@ -31,3 +31,32 @@ h1, code, li { .sidebar-logo{ padding: 20% 15%; } + +/* Supporting style for replacing admonition titles from

to

. */ +h2.admonition-title { + position: relative; + margin: 0 -0.5rem 0.5rem; + padding-left: 2.75rem; + padding-right: .5rem; + padding-top: .4rem; + padding-bottom: .4rem; + font-weight: 700; + font-size: 1.5rem; + line-height: 1.25; + border-radius: unset; +} +h2.admonition-title::before { + content: ""; + position: absolute; + left: 0.5rem; + width: 1.75rem; + height: 1.75rem; +} +h2.admonition-title { + background-color: var(--color-admonition-title-background); +} +h2.admonition-title::before { + background-color: var(--color-admonition-title); + mask-image: var(--icon-admonition-default); + mask-repeat: no-repeat; +} diff --git a/docs/source/_static/js/custom.js b/docs/source/_static/js/custom.js index 6596a718e1..ee8839a340 100644 --- a/docs/source/_static/js/custom.js +++ b/docs/source/_static/js/custom.js @@ -112,18 +112,41 @@ function makeCodeBlocksScrollable() { codeCell.tabIndex = 0; }); } +// Replaces an HTML element in the DOM with a new element of a different tag +// type while preserving the original element's attributes and content. +function replaceElementWithNewTagType(originalElement, newTag) { + // Create a new element with the requested tag. + const newElement = document.createElement(newTag); + // Copy over the attributes of the original element. + for (const attribute of originalElement.attributes) { + newElement.setAttribute(attribute.name, attribute.value); + } + // Copy over the content of the original title element + newElement.innerHTML = originalElement.innerHTML; + // Replace the original element with the new element in the DOM + originalElement.parentNode.replaceChild(newElement, originalElement); +} // Converts implicit bold headings into actual headings with h3 tags. function convertImplicitHeadings() { boldElements.forEach(boldElement => { if (headings.includes(boldElement.innerHTML)) { - boldElement.parentElement.outerHTML = `

${ boldElement.innerHTML }

`; + replaceElementWithNewTagType(boldElement, 'h3'); } }); } +// Converts admonition titles from p tags to h2 tags. +function convertAdmonitionTitlesToHeadings() { + const admonitionTitleSelector = '.admonition-title'; + const admonitionTitleElements = document.querySelectorAll(admonitionTitleSelector); + admonitionTitleElements.forEach(originalElement => { + replaceElementWithNewTagType(originalElement, 'h2'); + }); +} // Functions to run after the DOM loads. function runAfterDOMLoads() { expandSubMenu(); convertImplicitHeadings(); + convertAdmonitionTitlesToHeadings() makeCodeBlocksScrollable(); } // Run a function after the DOM loads. From 64aa6ca2497bef64eca7b3ba8b6c3c0b1899e7fb Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Wed, 3 May 2023 21:59:46 -0700 Subject: [PATCH 02/13] Use to make customizations at generation time. --- botocore/docs/bcdoc/style.py | 62 +++++++++++++++++++++------ docs/source/_static/js/custom.js | 44 ------------------- tests/functional/docs/test_lex.py | 6 ++- tests/unit/docs/bcdoc/test_style.py | 19 ++++++-- tests/unit/docs/test_client.py | 22 +++++++--- tests/unit/docs/test_method.py | 55 ++++++++++++++++-------- tests/unit/docs/test_paginator.py | 9 ++-- tests/unit/docs/test_service.py | 3 +- tests/unit/docs/test_sharedexample.py | 3 +- tests/unit/docs/test_waiter.py | 3 +- tests/unit/test_client.py | 9 ++-- tests/unit/test_waiters.py | 3 +- 12 files changed, 142 insertions(+), 96 deletions(-) diff --git a/botocore/docs/bcdoc/style.py b/botocore/docs/bcdoc/style.py index f2a165a932..a7ac0275d1 100644 --- a/botocore/docs/bcdoc/style.py +++ b/botocore/docs/bcdoc/style.py @@ -16,6 +16,17 @@ logger = logging.getLogger('bcdocs') # Terminal punctuation where a space is not needed before. PUNCTUATION_CHARACTERS = ('.', ',', '?', '!', ':', ';') +# Values that will be converted to HTML h3 tags. +BOLD_TO_H3_VALUES = [ + 'Example', + 'Examples', + 'Exceptions', + 'Request Syntax', + 'Response Structure', + 'Response Syntax', + 'Structure', + 'Syntax', +] class BaseStyle: @@ -125,11 +136,23 @@ def end_b(self): self.doc.do_translation = False self.end_bold() + def write_raw_h3(self, s): + self.doc.write('.. raw:: html') + self.indent() + self.new_paragraph() + self.doc.write(f"

{s}

") + self.dedent() + def bold(self, s): if s: - self.start_bold() - self.doc.write(s) - self.end_bold() + if s in BOLD_TO_H3_VALUES: + self.new_paragraph() + self.write_raw_h3(s) + self.new_paragraph() + else: + self.start_bold() + self.doc.write(s) + self.end_bold() def ref(self, title, link=None): if link is None: @@ -186,34 +209,49 @@ def code(self, s): self.doc.write(s) self.end_code() - def start_note(self, attrs=None): + def start_raw_admonition(self, type): + self.doc.write('.. raw:: html') + self.indent() self.new_paragraph() - self.doc.write('.. note::') + self.doc.write(f'
') + self.doc.write(f'

{type.title()}

') + self.dedent() + + def end_raw_admonition(self): + self.doc.write('.. raw:: html') self.indent() self.new_paragraph() + self.doc.write(f'
') + self.dedent() + + def start_note(self, attrs=None): + self.new_paragraph() + self.start_raw_admonition('note') + self.new_paragraph() def end_note(self): - self.dedent() + self.new_paragraph() + self.end_raw_admonition() self.new_paragraph() def start_important(self, attrs=None): self.new_paragraph() - self.doc.write('.. warning::') - self.indent() + self.start_raw_admonition('warning') self.new_paragraph() def end_important(self): - self.dedent() + self.new_paragraph() + self.end_raw_admonition() self.new_paragraph() def start_danger(self, attrs=None): self.new_paragraph() - self.doc.write('.. danger::') - self.indent() + self.start_raw_admonition('danger') self.new_paragraph() def end_danger(self): - self.dedent() + self.new_paragraph() + self.end_raw_admonition() self.new_paragraph() def start_a(self, attrs=None): diff --git a/docs/source/_static/js/custom.js b/docs/source/_static/js/custom.js index ee8839a340..7d876350c2 100644 --- a/docs/source/_static/js/custom.js +++ b/docs/source/_static/js/custom.js @@ -82,18 +82,6 @@ function makeServiceLinkCurrent(serviceName) { } const currentPagePath = window.location.pathname.split('/'); const codeBlockSelector = 'div.highlight pre'; -const boldTextSelector = 'strong'; -const boldElements = document.querySelectorAll(boldTextSelector); -const headings = [ - 'Example', - 'Examples', - 'Exceptions', - 'Request Syntax', - 'Response Structure', - 'Response Syntax', - 'Structure', - 'Syntax' -]; // Expands the "Available Services" sub-menu in the side-bar when viewing // nested doc pages and highlights the corresponding service list item. function expandSubMenu() { @@ -112,41 +100,9 @@ function makeCodeBlocksScrollable() { codeCell.tabIndex = 0; }); } -// Replaces an HTML element in the DOM with a new element of a different tag -// type while preserving the original element's attributes and content. -function replaceElementWithNewTagType(originalElement, newTag) { - // Create a new element with the requested tag. - const newElement = document.createElement(newTag); - // Copy over the attributes of the original element. - for (const attribute of originalElement.attributes) { - newElement.setAttribute(attribute.name, attribute.value); - } - // Copy over the content of the original title element - newElement.innerHTML = originalElement.innerHTML; - // Replace the original element with the new element in the DOM - originalElement.parentNode.replaceChild(newElement, originalElement); -} -// Converts implicit bold headings into actual headings with h3 tags. -function convertImplicitHeadings() { - boldElements.forEach(boldElement => { - if (headings.includes(boldElement.innerHTML)) { - replaceElementWithNewTagType(boldElement, 'h3'); - } - }); -} -// Converts admonition titles from p tags to h2 tags. -function convertAdmonitionTitlesToHeadings() { - const admonitionTitleSelector = '.admonition-title'; - const admonitionTitleElements = document.querySelectorAll(admonitionTitleSelector); - admonitionTitleElements.forEach(originalElement => { - replaceElementWithNewTagType(originalElement, 'h2'); - }); -} // Functions to run after the DOM loads. function runAfterDOMLoads() { expandSubMenu(); - convertImplicitHeadings(); - convertAdmonitionTitlesToHeadings() makeCodeBlocksScrollable(); } // Run a function after the DOM loads. diff --git a/tests/functional/docs/test_lex.py b/tests/functional/docs/test_lex.py index d7a792021b..e9bed9d10f 100644 --- a/tests/functional/docs/test_lex.py +++ b/tests/functional/docs/test_lex.py @@ -20,10 +20,12 @@ def test_jsonheader_docs(self): docs = self.get_docstring_for_method('lex-runtime', 'post_content') self.assert_contains_lines_in_order( [ - '**Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', 'sessionAttributes=%s,' % self.TYPE_STRING, ':type sessionAttributes: JSON serializable', - '**Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', '\'slots\': %s,' % self.TYPE_STRING, '\'sessionAttributes\': %s' % self.TYPE_STRING, '**slots** (JSON serializable)', diff --git a/tests/unit/docs/bcdoc/test_style.py b/tests/unit/docs/bcdoc/test_style.py index 05bf179f56..dbc08a244c 100644 --- a/tests/unit/docs/bcdoc/test_style.py +++ b/tests/unit/docs/bcdoc/test_style.py @@ -128,19 +128,32 @@ def test_important(self): style = ReSTStyle(ReSTDocument()) style.start_important() style.end_important() - self.assertEqual(style.doc.getvalue(), b'\n\n.. warning::\n\n \n\n') + self.assertEqual(style.doc.getvalue(), ( + b'\n\n.. raw:: html\n\n' + b'

Warning

\n\n' + b'\n\n.. raw:: html\n\n
\n\n' + )) def test_note(self): style = ReSTStyle(ReSTDocument()) style.start_note() style.end_note() - self.assertEqual(style.doc.getvalue(), b'\n\n.. note::\n\n \n\n') + self.assertEqual(style.doc.getvalue(), ( + b'\n\n.. raw:: html\n\n' + b'

Note

\n\n' + b'\n\n.. raw:: html\n\n
\n\n' + )) def test_danger(self): style = ReSTStyle(ReSTDocument()) style.start_danger() style.end_danger() - self.assertEqual(style.doc.getvalue(), b'\n\n.. danger::\n\n \n\n') + print(style.doc.getvalue()) + self.assertEqual(style.doc.getvalue(), ( + b'\n\n.. raw:: html\n\n' + b'

Danger

\n\n' + b'\n\n.. raw:: html\n\n
\n\n' + )) def test_toctree_html(self): style = ReSTStyle(ReSTDocument()) diff --git a/tests/unit/docs/test_client.py b/tests/unit/docs/test_client.py index c80cebc6e6..785cc59090 100644 --- a/tests/unit/docs/test_client.py +++ b/tests/unit/docs/test_client.py @@ -71,7 +71,8 @@ def test_document_client(self): self.assert_contains_lines_in_order( [ '.. py:method:: MyService.Client.sample_operation(**kwargs)', - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.sample_operation(', ' Biz=\'string\'', @@ -80,15 +81,18 @@ def test_document_client(self): ' :param Biz:', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Biz\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Biz** *(string) --*', - '**Exceptions**', + ' .. raw:: html', + '

Exceptions

', '* :py:class:`MyService.Client.exceptions.SomeException`', ], self.get_nested_service_contents( @@ -144,10 +148,13 @@ def test_modeled_exceptions(self): self.assert_contains_lines_in_order( [ '.. py:class:: MyService.Client.exceptions.SomeException', - '**Example**::', + ' .. raw:: html', + '

Example

', + '::', 'except client.exceptions.SomeException as e:', '.. py:attribute:: response', - '**Syntax**', + ' .. raw:: html', + '

Syntax

', '{', "'Message': 'string',", "'Error': {", @@ -155,7 +162,8 @@ def test_modeled_exceptions(self): "'Message': 'string'", '}', '}', - '**Structure**', + ' .. raw:: html', + '

Structure

', '- *(dict) --*', '- **Message** *(string) --* ', '- **Error** *(dict) --* ', diff --git a/tests/unit/docs/test_method.py b/tests/unit/docs/test_method.py index a23fb98817..572eed6fc2 100644 --- a/tests/unit/docs/test_method.py +++ b/tests/unit/docs/test_method.py @@ -142,7 +142,8 @@ def test_default(self): '.. py:method:: foo(**kwargs)', ' This describes the foo method.', cross_ref_link, - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.foo(', ' Bar=\'string\'', @@ -151,12 +152,14 @@ def test_default(self): ' :param Bar:', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Bar\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Bar** *(string) --*', ] @@ -177,7 +180,8 @@ def test_no_input_output_shape(self): [ '.. py:method:: foo()', ' This describes the foo method.', - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.foo()', ' :returns: None', @@ -203,7 +207,8 @@ def test_include_input(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.foo(', ' Bar=\'string\',', @@ -215,12 +220,14 @@ def test_include_input(self): ' :param Biz: biz docs', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Bar\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Bar** *(string) --*', ] @@ -245,7 +252,8 @@ def test_include_output(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.foo(', ' Bar=\'string\'', @@ -254,13 +262,15 @@ def test_include_output(self): ' :param Bar:', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Bar\': \'string\'', ' \'Biz\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Bar** *(string) --*', ' - **Biz** *(string) --*', @@ -282,7 +292,8 @@ def test_exclude_input(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.foo(', ' Biz=\'string\'', @@ -291,13 +302,15 @@ def test_exclude_input(self): ' :param Biz:', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Bar\': \'string\'', ' \'Biz\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Bar** *(string) --*', ' - **Biz** *(string) --*', @@ -322,7 +335,8 @@ def test_exclude_output(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response = client.foo(', ' Bar=\'string\'', @@ -332,12 +346,14 @@ def test_exclude_output(self): ' :param Biz:', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Biz\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Biz** *(string) --*', ] @@ -447,9 +463,12 @@ def test_deprecated(self): # The line in the example self.assert_contains_lines_in_order( [ - ' .. danger::', - ' This operation is deprecated and may not function as ' + ' .. raw:: html', + '

Danger

', + ' This operation is deprecated and may not function as ' 'expected. This operation should not be used going forward and is ' 'only kept for the purpose of backwards compatiblity.', + ' .. raw:: html', + '
' ] ) diff --git a/tests/unit/docs/test_paginator.py b/tests/unit/docs/test_paginator.py index ec420577be..86508ab716 100644 --- a/tests/unit/docs/test_paginator.py +++ b/tests/unit/docs/test_paginator.py @@ -51,7 +51,8 @@ def test_document_paginators(self): ' Creates an iterator that will paginate through responses' ' from :py:meth:`MyService.Client.sample_operation`.' ), - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response_iterator = paginator.paginate(', ' Biz=\'string\',', @@ -74,13 +75,15 @@ def test_document_paginators(self): ' - **StartingToken** *(string) --*', ' :rtype: dict', ' :returns:', - ' **Response Syntax**', + ' .. raw:: html', + '

Response Syntax

', ' ::', ' {', ' \'Biz\': \'string\',', ' \'NextToken\': \'string\'', ' }', - ' **Response Structure**', + ' .. raw:: html', + '

Response Structure

', ' - *(dict) --*', ' - **Biz** *(string) --*', ' - **NextToken** *(string) --*', diff --git a/tests/unit/docs/test_service.py b/tests/unit/docs/test_service.py index ebdb1d4357..4fdc1b9603 100644 --- a/tests/unit/docs/test_service.py +++ b/tests/unit/docs/test_service.py @@ -78,7 +78,8 @@ def test_document_service(self): self.assert_contains_lines_in_order( [ '.. py:method:: MyService.Client.sample_operation(**kwargs)', - ' **Examples**', + ' .. raw:: html', + '

Examples

', ' Sample Description.', ' ::', ' response = client.sample_operation(', diff --git a/tests/unit/docs/test_sharedexample.py b/tests/unit/docs/test_sharedexample.py index 0fe6a773f2..d2e32bb03a 100644 --- a/tests/unit/docs/test_sharedexample.py +++ b/tests/unit/docs/test_sharedexample.py @@ -68,7 +68,8 @@ def test_default(self): ) self.assert_contains_lines_in_order( [ - "**Examples**", + '.. raw:: html', + '

Examples

', "Sample Description.", "::", " response = client.foo(", diff --git a/tests/unit/docs/test_waiter.py b/tests/unit/docs/test_waiter.py index f23985e1c9..3ce3aea6ac 100644 --- a/tests/unit/docs/test_waiter.py +++ b/tests/unit/docs/test_waiter.py @@ -49,7 +49,8 @@ def test_document_waiters(self): 'every 15 seconds until a successful state is reached. An error ' 'is returned after 40 failed checks.' ), - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' waiter.wait(', ' Biz=\'string\'', diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index eb7a30787b..ed26ab7b06 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -287,7 +287,8 @@ def test_client_method_docstring(self): method_docstring = str(service_client.test_operation.__doc__) ref_docstring_lines = [ 'Documents TestOperation', - '**Request Syntax**', + '.. raw:: html', + '

Request Syntax

', ' response = client.test_operation(', ' Bar=\'string\'', ' Foo=\'string\'', @@ -310,7 +311,8 @@ def test_client_method_help(self): method_docstring = mock_stdout.getvalue() ref_docstring_lines = [ 'Documents TestOperation', - '**Request Syntax**', + '.. raw:: html', + '

Request Syntax

', ' response = client.test_operation(', ' Bar=\'string\'', ' Foo=\'string\'', @@ -1213,7 +1215,8 @@ def test_paginator_help_from_client(self): ' Creates an iterator that will paginate through responses ' 'from :py:meth:`MyService.Client.test_operation`.' ), - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' response_iterator = paginator.paginate(', " Foo='string',", diff --git a/tests/unit/test_waiters.py b/tests/unit/test_waiters.py index 62544e180a..611647572f 100644 --- a/tests/unit/test_waiters.py +++ b/tests/unit/test_waiters.py @@ -771,7 +771,8 @@ def test_waiter_help_documentation(self): 'seconds until a successful state is reached. An error ' 'is returned after 1 failed checks.' ), - ' **Request Syntax**', + ' .. raw:: html', + '

Request Syntax

', ' ::', ' waiter.wait(', " bar='string'", From 029c7c7986ee2ecdf8fa3b1e2b5ca5c0e305a8ab Mon Sep 17 00:00:00 2001 From: Jonathan Gaytan Date: Wed, 3 May 2023 22:29:01 -0700 Subject: [PATCH 03/13] Ran linter --- tests/unit/docs/bcdoc/test_style.py | 39 ++++++++++++++++++----------- tests/unit/docs/test_method.py | 2 +- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/tests/unit/docs/bcdoc/test_style.py b/tests/unit/docs/bcdoc/test_style.py index dbc08a244c..93ea8b28e7 100644 --- a/tests/unit/docs/bcdoc/test_style.py +++ b/tests/unit/docs/bcdoc/test_style.py @@ -128,32 +128,41 @@ def test_important(self): style = ReSTStyle(ReSTDocument()) style.start_important() style.end_important() - self.assertEqual(style.doc.getvalue(), ( - b'\n\n.. raw:: html\n\n' - b'

Warning

\n\n' - b'\n\n.. raw:: html\n\n
\n\n' - )) + self.assertEqual( + style.doc.getvalue(), + ( + b'\n\n.. raw:: html\n\n' + b'

Warning

\n\n' + b'\n\n.. raw:: html\n\n
\n\n' + ), + ) def test_note(self): style = ReSTStyle(ReSTDocument()) style.start_note() style.end_note() - self.assertEqual(style.doc.getvalue(), ( - b'\n\n.. raw:: html\n\n' - b'

Note

\n\n' - b'\n\n.. raw:: html\n\n
\n\n' - )) + self.assertEqual( + style.doc.getvalue(), + ( + b'\n\n.. raw:: html\n\n' + b'

Note

\n\n' + b'\n\n.. raw:: html\n\n
\n\n' + ), + ) def test_danger(self): style = ReSTStyle(ReSTDocument()) style.start_danger() style.end_danger() print(style.doc.getvalue()) - self.assertEqual(style.doc.getvalue(), ( - b'\n\n.. raw:: html\n\n' - b'

Danger

\n\n' - b'\n\n.. raw:: html\n\n
\n\n' - )) + self.assertEqual( + style.doc.getvalue(), + ( + b'\n\n.. raw:: html\n\n' + b'

Danger

\n\n' + b'\n\n.. raw:: html\n\n
\n\n' + ), + ) def test_toctree_html(self): style = ReSTStyle(ReSTDocument()) diff --git a/tests/unit/docs/test_method.py b/tests/unit/docs/test_method.py index 572eed6fc2..38feb04a1c 100644 --- a/tests/unit/docs/test_method.py +++ b/tests/unit/docs/test_method.py @@ -469,6 +469,6 @@ def test_deprecated(self): 'expected. This operation should not be used going forward and is ' 'only kept for the purpose of backwards compatiblity.', ' .. raw:: html', - ' ' + ' ', ] ) From 1fa06a566ab494d5c196c04b1d6a8041797de797 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Wed, 3 May 2023 22:32:35 -0700 Subject: [PATCH 04/13] Missed linting issue. --- botocore/docs/bcdoc/style.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/botocore/docs/bcdoc/style.py b/botocore/docs/bcdoc/style.py index a7ac0275d1..0b934cbc6b 100644 --- a/botocore/docs/bcdoc/style.py +++ b/botocore/docs/bcdoc/style.py @@ -221,7 +221,7 @@ def end_raw_admonition(self): self.doc.write('.. raw:: html') self.indent() self.new_paragraph() - self.doc.write(f'') + self.doc.write('') self.dedent() def start_note(self, attrs=None): From 1055551ffe706566db4ceec1f0f705ac03bd1c41 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Thu, 4 May 2023 10:39:32 -0700 Subject: [PATCH 05/13] Used html directly. Changed Note title to h3. --- botocore/docs/bcdoc/style.py | 26 ++++++-------------------- botocore/docs/client.py | 8 ++++---- botocore/docs/method.py | 6 +++--- botocore/docs/sharedexample.py | 2 +- docs/source/_static/css/custom.css | 18 +++++++++--------- tests/unit/docs/bcdoc/test_style.py | 6 +++--- tests/unit/docs/test_method.py | 2 +- 7 files changed, 27 insertions(+), 41 deletions(-) diff --git a/botocore/docs/bcdoc/style.py b/botocore/docs/bcdoc/style.py index 0b934cbc6b..866257873c 100644 --- a/botocore/docs/bcdoc/style.py +++ b/botocore/docs/bcdoc/style.py @@ -16,17 +16,6 @@ logger = logging.getLogger('bcdocs') # Terminal punctuation where a space is not needed before. PUNCTUATION_CHARACTERS = ('.', ',', '?', '!', ':', ';') -# Values that will be converted to HTML h3 tags. -BOLD_TO_H3_VALUES = [ - 'Example', - 'Examples', - 'Exceptions', - 'Request Syntax', - 'Response Structure', - 'Response Syntax', - 'Structure', - 'Syntax', -] class BaseStyle: @@ -137,22 +126,19 @@ def end_b(self): self.end_bold() def write_raw_h3(self, s): + self.new_paragraph() self.doc.write('.. raw:: html') self.indent() self.new_paragraph() self.doc.write(f"

{s}

") self.dedent() + self.new_paragraph() def bold(self, s): if s: - if s in BOLD_TO_H3_VALUES: - self.new_paragraph() - self.write_raw_h3(s) - self.new_paragraph() - else: - self.start_bold() - self.doc.write(s) - self.end_bold() + self.start_bold() + self.doc.write(s) + self.end_bold() def ref(self, title, link=None): if link is None: @@ -214,7 +200,7 @@ def start_raw_admonition(self, type): self.indent() self.new_paragraph() self.doc.write(f'
') - self.doc.write(f'

{type.title()}

') + self.doc.write(f'

{type.title()}

') self.dedent() def end_raw_admonition(self): diff --git a/botocore/docs/client.py b/botocore/docs/client.py index ba2366a846..46457e9cfc 100644 --- a/botocore/docs/client.py +++ b/botocore/docs/client.py @@ -170,7 +170,7 @@ def _add_custom_method(self, section, method_name, method): def _add_method_exceptions_list(self, section, operation_model): error_section = section.add_new_section('exceptions') error_section.style.new_line() - error_section.style.bold('Exceptions') + error_section.style.write_raw_h3('Exceptions') error_section.style.new_line() for error in operation_model.error_shapes: class_name = ( @@ -334,7 +334,7 @@ def _add_top_level_documentation(self, section, shape): def _add_exception_catch_example(self, section, shape): section.style.new_line() - section.style.bold('Example') + section.style.write_raw_h3('Example') section.style.start_codeblock() section.write('try:') section.style.indent() @@ -370,7 +370,7 @@ def _add_response_attr_description(self, section): def _add_response_example(self, section, shape): example_section = section.add_new_section('syntax') example_section.style.new_line() - example_section.style.bold('Syntax') + example_section.style.write_raw_h3('Syntax') example_section.style.new_paragraph() documenter = ResponseExampleDocumenter( service_name=self._service_name, @@ -386,7 +386,7 @@ def _add_response_example(self, section, shape): def _add_response_params(self, section, shape): params_section = section.add_new_section('Structure') params_section.style.new_line() - params_section.style.bold('Structure') + params_section.style.write_raw_h3('Structure') params_section.style.new_paragraph() documenter = ResponseParamsDocumenter( service_name=self._service_name, diff --git a/botocore/docs/method.py b/botocore/docs/method.py index 5db906c8dd..8c4df8790d 100644 --- a/botocore/docs/method.py +++ b/botocore/docs/method.py @@ -220,7 +220,7 @@ def document_model_driven_method( # Add the example section. example_section = section.add_new_section('request-example') example_section.style.new_paragraph() - example_section.style.bold('Request Syntax') + example_section.style.write_raw_h3('Request Syntax') context = { 'special_shape_types': { @@ -292,7 +292,7 @@ def document_model_driven_method( 'response-example' ) return_example_section.style.new_line() - return_example_section.style.bold('Response Syntax') + return_example_section.style.write_raw_h3('Response Syntax') return_example_section.style.new_paragraph() ResponseExampleDocumenter( service_name=operation_model.service_model.service_name, @@ -311,7 +311,7 @@ def document_model_driven_method( 'description' ) return_description_section.style.new_line() - return_description_section.style.bold('Response Structure') + return_description_section.style.write_raw_h3('Response Structure') return_description_section.style.new_paragraph() ResponseParamsDocumenter( service_name=operation_model.service_model.service_name, diff --git a/botocore/docs/sharedexample.py b/botocore/docs/sharedexample.py index 58cdfa594c..af14097779 100644 --- a/botocore/docs/sharedexample.py +++ b/botocore/docs/sharedexample.py @@ -216,7 +216,7 @@ def document_shared_examples( """ container_section = section.add_new_section('shared-examples') container_section.style.new_paragraph() - container_section.style.bold('Examples') + container_section.style.write_raw_h3('Examples') documenter = SharedExampleDocumenter() for example in shared_examples: documenter.document_shared_example( diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css index 7497773a31..0acedf960d 100644 --- a/docs/source/_static/css/custom.css +++ b/docs/source/_static/css/custom.css @@ -32,30 +32,30 @@ h1, code, li { padding: 20% 15%; } -/* Supporting style for replacing admonition titles from

to

. */ -h2.admonition-title { +/* Supporting style for replacing admonition titles from

to

. */ +h3.admonition-title { position: relative; margin: 0 -0.5rem 0.5rem; - padding-left: 2.75rem; + padding-left: 2.5rem; padding-right: .5rem; padding-top: .4rem; padding-bottom: .4rem; font-weight: 700; - font-size: 1.5rem; + font-size: 1.5em; line-height: 1.25; border-radius: unset; } -h2.admonition-title::before { +h3.admonition-title::before { content: ""; position: absolute; left: 0.5rem; - width: 1.75rem; - height: 1.75rem; + width: 1.5rem; + height: 1.5rem; } -h2.admonition-title { +h3.admonition-title { background-color: var(--color-admonition-title-background); } -h2.admonition-title::before { +h3.admonition-title::before { background-color: var(--color-admonition-title); mask-image: var(--icon-admonition-default); mask-repeat: no-repeat; diff --git a/tests/unit/docs/bcdoc/test_style.py b/tests/unit/docs/bcdoc/test_style.py index 93ea8b28e7..80091f5593 100644 --- a/tests/unit/docs/bcdoc/test_style.py +++ b/tests/unit/docs/bcdoc/test_style.py @@ -132,7 +132,7 @@ def test_important(self): style.doc.getvalue(), ( b'\n\n.. raw:: html\n\n' - b'

Warning

\n\n' + b'

Warning

\n\n' b'\n\n.. raw:: html\n\n
\n\n' ), ) @@ -145,7 +145,7 @@ def test_note(self): style.doc.getvalue(), ( b'\n\n.. raw:: html\n\n' - b'

Note

\n\n' + b'

Note

\n\n' b'\n\n.. raw:: html\n\n
\n\n' ), ) @@ -159,7 +159,7 @@ def test_danger(self): style.doc.getvalue(), ( b'\n\n.. raw:: html\n\n' - b'

Danger

\n\n' + b'

Danger

\n\n' b'\n\n.. raw:: html\n\n
\n\n' ), ) diff --git a/tests/unit/docs/test_method.py b/tests/unit/docs/test_method.py index 38feb04a1c..e3102f5c02 100644 --- a/tests/unit/docs/test_method.py +++ b/tests/unit/docs/test_method.py @@ -464,7 +464,7 @@ def test_deprecated(self): self.assert_contains_lines_in_order( [ ' .. raw:: html', - '

Danger

', + '

Danger

', ' This operation is deprecated and may not function as ' 'expected. This operation should not be used going forward and is ' 'only kept for the purpose of backwards compatiblity.', From f0efe8527a0758e2966d5dc7022b4dd845a95020 Mon Sep 17 00:00:00 2001 From: Jonathan Gaytan Date: Thu, 4 May 2023 11:46:07 -0700 Subject: [PATCH 06/13] Wrote reusable components for the raw directive. --- botocore/docs/bcdoc/style.py | 39 +++++++++++------------------ tests/unit/docs/bcdoc/test_style.py | 9 ++++--- tests/unit/docs/test_method.py | 3 ++- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/botocore/docs/bcdoc/style.py b/botocore/docs/bcdoc/style.py index 866257873c..3abd596c00 100644 --- a/botocore/docs/bcdoc/style.py +++ b/botocore/docs/bcdoc/style.py @@ -126,13 +126,9 @@ def end_b(self): self.end_bold() def write_raw_h3(self, s): - self.new_paragraph() - self.doc.write('.. raw:: html') - self.indent() - self.new_paragraph() + self.start_raw_directive() self.doc.write(f"

{s}

") - self.dedent() - self.new_paragraph() + self.end_raw_directive() def bold(self, s): if s: @@ -195,50 +191,45 @@ def code(self, s): self.doc.write(s) self.end_code() - def start_raw_admonition(self, type): + def start_raw_directive(self): + self.new_paragraph() self.doc.write('.. raw:: html') self.indent() self.new_paragraph() + + def end_raw_directive(self): + self.dedent() + self.new_paragraph() + + def start_raw_admonition(self, type): + self.start_raw_directive() self.doc.write(f'
') + self.new_line() self.doc.write(f'

{type.title()}

') - self.dedent() + self.end_raw_directive() def end_raw_admonition(self): - self.doc.write('.. raw:: html') - self.indent() - self.new_paragraph() + self.start_raw_directive() self.doc.write('
') - self.dedent() + self.end_raw_directive() def start_note(self, attrs=None): - self.new_paragraph() self.start_raw_admonition('note') - self.new_paragraph() def end_note(self): - self.new_paragraph() self.end_raw_admonition() - self.new_paragraph() def start_important(self, attrs=None): - self.new_paragraph() self.start_raw_admonition('warning') - self.new_paragraph() def end_important(self): - self.new_paragraph() self.end_raw_admonition() - self.new_paragraph() def start_danger(self, attrs=None): - self.new_paragraph() self.start_raw_admonition('danger') - self.new_paragraph() def end_danger(self): - self.new_paragraph() self.end_raw_admonition() - self.new_paragraph() def start_a(self, attrs=None): # Write an empty space to guard against zero whitespace diff --git a/tests/unit/docs/bcdoc/test_style.py b/tests/unit/docs/bcdoc/test_style.py index 80091f5593..c1d2a1e0a1 100644 --- a/tests/unit/docs/bcdoc/test_style.py +++ b/tests/unit/docs/bcdoc/test_style.py @@ -132,7 +132,8 @@ def test_important(self): style.doc.getvalue(), ( b'\n\n.. raw:: html\n\n' - b'

Warning

\n\n' + b'
\n' + b'

Warning

\n\n' b'\n\n.. raw:: html\n\n
\n\n' ), ) @@ -145,7 +146,8 @@ def test_note(self): style.doc.getvalue(), ( b'\n\n.. raw:: html\n\n' - b'

Note

\n\n' + b'
\n' + b'

Note

\n\n' b'\n\n.. raw:: html\n\n
\n\n' ), ) @@ -159,7 +161,8 @@ def test_danger(self): style.doc.getvalue(), ( b'\n\n.. raw:: html\n\n' - b'

Danger

\n\n' + b'
\n' + b'

Danger

\n\n' b'\n\n.. raw:: html\n\n
\n\n' ), ) diff --git a/tests/unit/docs/test_method.py b/tests/unit/docs/test_method.py index e3102f5c02..794ba85ed9 100644 --- a/tests/unit/docs/test_method.py +++ b/tests/unit/docs/test_method.py @@ -464,7 +464,8 @@ def test_deprecated(self): self.assert_contains_lines_in_order( [ ' .. raw:: html', - '

Danger

', + '
', + '

Danger

', ' This operation is deprecated and may not function as ' 'expected. This operation should not be used going forward and is ' 'only kept for the purpose of backwards compatiblity.', From 9e22a73b0f93f38edc91f1b8c0ac978b39e90574 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Fri, 5 May 2023 10:29:12 -0700 Subject: [PATCH 07/13] PR feedback --- docs/source/_static/css/custom.css | 9 +++------ tests/unit/docs/bcdoc/test_style.py | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css index 0acedf960d..357d1ec0d4 100644 --- a/docs/source/_static/css/custom.css +++ b/docs/source/_static/css/custom.css @@ -32,7 +32,7 @@ h1, code, li { padding: 20% 15%; } -/* Supporting style for replacing admonition titles from

to

. */ +/* Apply furo styled admonition titles for

. */ h3.admonition-title { position: relative; margin: 0 -0.5rem 0.5rem; @@ -44,18 +44,15 @@ h3.admonition-title { font-size: 1.5em; line-height: 1.25; border-radius: unset; + background-color: var(--color-admonition-title-background); } +/* Apply furo styled admonition icons before

. */ h3.admonition-title::before { content: ""; position: absolute; left: 0.5rem; width: 1.5rem; height: 1.5rem; -} -h3.admonition-title { - background-color: var(--color-admonition-title-background); -} -h3.admonition-title::before { background-color: var(--color-admonition-title); mask-image: var(--icon-admonition-default); mask-repeat: no-repeat; diff --git a/tests/unit/docs/bcdoc/test_style.py b/tests/unit/docs/bcdoc/test_style.py index c1d2a1e0a1..89f1caccd3 100644 --- a/tests/unit/docs/bcdoc/test_style.py +++ b/tests/unit/docs/bcdoc/test_style.py @@ -156,7 +156,6 @@ def test_danger(self): style = ReSTStyle(ReSTDocument()) style.start_danger() style.end_danger() - print(style.doc.getvalue()) self.assertEqual( style.doc.getvalue(), ( From be90ff4b8009acac0b349b01fd978fd9490d9c4f Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Mon, 8 May 2023 12:32:14 -0700 Subject: [PATCH 08/13] New approach: Extend Sphinx HTML5Translator instead of using raw directive. --- botocore/docs/bcdoc/style.py | 45 +++++++-------------- botocore/docs/client.py | 8 ++-- botocore/docs/method.py | 6 +-- botocore/docs/sharedexample.py | 2 +- docs/source/conf.py | 52 +++++++++++++++++++++++++ tests/functional/docs/test_lex.py | 6 +-- tests/unit/docs/bcdoc/test_style.py | 30 ++------------ tests/unit/docs/test_client.py | 22 ++++------- tests/unit/docs/test_method.py | 56 +++++++++------------------ tests/unit/docs/test_paginator.py | 9 ++--- tests/unit/docs/test_service.py | 3 +- tests/unit/docs/test_sharedexample.py | 3 +- tests/unit/docs/test_waiter.py | 3 +- tests/unit/test_client.py | 9 ++--- tests/unit/test_waiters.py | 3 +- 15 files changed, 115 insertions(+), 142 deletions(-) diff --git a/botocore/docs/bcdoc/style.py b/botocore/docs/bcdoc/style.py index 3abd596c00..f2a165a932 100644 --- a/botocore/docs/bcdoc/style.py +++ b/botocore/docs/bcdoc/style.py @@ -125,11 +125,6 @@ def end_b(self): self.doc.do_translation = False self.end_bold() - def write_raw_h3(self, s): - self.start_raw_directive() - self.doc.write(f"

{s}

") - self.end_raw_directive() - def bold(self, s): if s: self.start_bold() @@ -191,45 +186,35 @@ def code(self, s): self.doc.write(s) self.end_code() - def start_raw_directive(self): + def start_note(self, attrs=None): self.new_paragraph() - self.doc.write('.. raw:: html') + self.doc.write('.. note::') self.indent() self.new_paragraph() - def end_raw_directive(self): + def end_note(self): self.dedent() self.new_paragraph() - def start_raw_admonition(self, type): - self.start_raw_directive() - self.doc.write(f'
') - self.new_line() - self.doc.write(f'

{type.title()}

') - self.end_raw_directive() - - def end_raw_admonition(self): - self.start_raw_directive() - self.doc.write('
') - self.end_raw_directive() - - def start_note(self, attrs=None): - self.start_raw_admonition('note') - - def end_note(self): - self.end_raw_admonition() - def start_important(self, attrs=None): - self.start_raw_admonition('warning') + self.new_paragraph() + self.doc.write('.. warning::') + self.indent() + self.new_paragraph() def end_important(self): - self.end_raw_admonition() + self.dedent() + self.new_paragraph() def start_danger(self, attrs=None): - self.start_raw_admonition('danger') + self.new_paragraph() + self.doc.write('.. danger::') + self.indent() + self.new_paragraph() def end_danger(self): - self.end_raw_admonition() + self.dedent() + self.new_paragraph() def start_a(self, attrs=None): # Write an empty space to guard against zero whitespace diff --git a/botocore/docs/client.py b/botocore/docs/client.py index 46457e9cfc..ba2366a846 100644 --- a/botocore/docs/client.py +++ b/botocore/docs/client.py @@ -170,7 +170,7 @@ def _add_custom_method(self, section, method_name, method): def _add_method_exceptions_list(self, section, operation_model): error_section = section.add_new_section('exceptions') error_section.style.new_line() - error_section.style.write_raw_h3('Exceptions') + error_section.style.bold('Exceptions') error_section.style.new_line() for error in operation_model.error_shapes: class_name = ( @@ -334,7 +334,7 @@ def _add_top_level_documentation(self, section, shape): def _add_exception_catch_example(self, section, shape): section.style.new_line() - section.style.write_raw_h3('Example') + section.style.bold('Example') section.style.start_codeblock() section.write('try:') section.style.indent() @@ -370,7 +370,7 @@ def _add_response_attr_description(self, section): def _add_response_example(self, section, shape): example_section = section.add_new_section('syntax') example_section.style.new_line() - example_section.style.write_raw_h3('Syntax') + example_section.style.bold('Syntax') example_section.style.new_paragraph() documenter = ResponseExampleDocumenter( service_name=self._service_name, @@ -386,7 +386,7 @@ def _add_response_example(self, section, shape): def _add_response_params(self, section, shape): params_section = section.add_new_section('Structure') params_section.style.new_line() - params_section.style.write_raw_h3('Structure') + params_section.style.bold('Structure') params_section.style.new_paragraph() documenter = ResponseParamsDocumenter( service_name=self._service_name, diff --git a/botocore/docs/method.py b/botocore/docs/method.py index 8c4df8790d..5db906c8dd 100644 --- a/botocore/docs/method.py +++ b/botocore/docs/method.py @@ -220,7 +220,7 @@ def document_model_driven_method( # Add the example section. example_section = section.add_new_section('request-example') example_section.style.new_paragraph() - example_section.style.write_raw_h3('Request Syntax') + example_section.style.bold('Request Syntax') context = { 'special_shape_types': { @@ -292,7 +292,7 @@ def document_model_driven_method( 'response-example' ) return_example_section.style.new_line() - return_example_section.style.write_raw_h3('Response Syntax') + return_example_section.style.bold('Response Syntax') return_example_section.style.new_paragraph() ResponseExampleDocumenter( service_name=operation_model.service_model.service_name, @@ -311,7 +311,7 @@ def document_model_driven_method( 'description' ) return_description_section.style.new_line() - return_description_section.style.write_raw_h3('Response Structure') + return_description_section.style.bold('Response Structure') return_description_section.style.new_paragraph() ResponseParamsDocumenter( service_name=operation_model.service_model.service_name, diff --git a/botocore/docs/sharedexample.py b/botocore/docs/sharedexample.py index af14097779..58cdfa594c 100644 --- a/botocore/docs/sharedexample.py +++ b/botocore/docs/sharedexample.py @@ -216,7 +216,7 @@ def document_shared_examples( """ container_section = section.add_new_section('shared-examples') container_section.style.new_paragraph() - container_section.style.write_raw_h3('Examples') + container_section.style.bold('Examples') documenter = SharedExampleDocumenter() for example in shared_examples: documenter.document_shared_example( diff --git a/docs/source/conf.py b/docs/source/conf.py index b2cb4eaa3f..8fd7244ef2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,6 +14,8 @@ import datetime, sys, os from botocore.session import get_session from botocore.docs import generate_docs +from sphinx.locale import admonitionlabels +from sphinx.writers.html5 import HTML5Translator as SphinxHTML5Translator generate_docs(os.path.dirname(os.path.abspath(__file__)), get_session()) @@ -283,3 +285,53 @@ # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' + +class HTML5Translator(SphinxHTML5Translator): + """ + This is our custom HTML5Translator which extends the one provided by Sphinx. + """ + STRONG_TO_H3_HEADINGS = [ + 'Example', + 'Examples', + 'Exceptions', + 'Request Syntax', + 'Response Structure', + 'Response Syntax', + 'Structure', + 'Syntax' + ] + + def visit_admonition(self, node, name=''): + """Uses the h3 tag for admonition titles instead of the p tag""" + self.body.append(self.starttag( + node, 'div', CLASS=('admonition ' + name))) + if name: + title = ( + f"

" + f"{admonitionlabels[name]}

" + ) + self.body.append(title) + + def visit_strong(self, node): + """ + Opens the h3 tag for a specific set of words/phrases and opens the + strong tag for all others. + """ + if node[0] in self.STRONG_TO_H3_HEADINGS: + self.body.append(self.starttag(node, 'h3', '')) + else: + self.body.append(self.starttag(node, 'strong', '')) + + def depart_strong(self, node): + """ + Closes the h3 tag for a specific set of words/phrases and closes the + strong tag for all others. + """ + if node[0] in self.STRONG_TO_H3_HEADINGS: + self.body.append('

') + else: + self.body.append('') + +def setup(app): + # Register our custom HTML translator. + app.set_translator('html', HTML5Translator) diff --git a/tests/functional/docs/test_lex.py b/tests/functional/docs/test_lex.py index e9bed9d10f..d7a792021b 100644 --- a/tests/functional/docs/test_lex.py +++ b/tests/functional/docs/test_lex.py @@ -20,12 +20,10 @@ def test_jsonheader_docs(self): docs = self.get_docstring_for_method('lex-runtime', 'post_content') self.assert_contains_lines_in_order( [ - ' .. raw:: html', - '

Request Syntax

', + '**Request Syntax**', 'sessionAttributes=%s,' % self.TYPE_STRING, ':type sessionAttributes: JSON serializable', - ' .. raw:: html', - '

Response Syntax

', + '**Response Syntax**', '\'slots\': %s,' % self.TYPE_STRING, '\'sessionAttributes\': %s' % self.TYPE_STRING, '**slots** (JSON serializable)', diff --git a/tests/unit/docs/bcdoc/test_style.py b/tests/unit/docs/bcdoc/test_style.py index 89f1caccd3..05bf179f56 100644 --- a/tests/unit/docs/bcdoc/test_style.py +++ b/tests/unit/docs/bcdoc/test_style.py @@ -128,43 +128,19 @@ def test_important(self): style = ReSTStyle(ReSTDocument()) style.start_important() style.end_important() - self.assertEqual( - style.doc.getvalue(), - ( - b'\n\n.. raw:: html\n\n' - b'
\n' - b'

Warning

\n\n' - b'\n\n.. raw:: html\n\n
\n\n' - ), - ) + self.assertEqual(style.doc.getvalue(), b'\n\n.. warning::\n\n \n\n') def test_note(self): style = ReSTStyle(ReSTDocument()) style.start_note() style.end_note() - self.assertEqual( - style.doc.getvalue(), - ( - b'\n\n.. raw:: html\n\n' - b'
\n' - b'

Note

\n\n' - b'\n\n.. raw:: html\n\n
\n\n' - ), - ) + self.assertEqual(style.doc.getvalue(), b'\n\n.. note::\n\n \n\n') def test_danger(self): style = ReSTStyle(ReSTDocument()) style.start_danger() style.end_danger() - self.assertEqual( - style.doc.getvalue(), - ( - b'\n\n.. raw:: html\n\n' - b'
\n' - b'

Danger

\n\n' - b'\n\n.. raw:: html\n\n
\n\n' - ), - ) + self.assertEqual(style.doc.getvalue(), b'\n\n.. danger::\n\n \n\n') def test_toctree_html(self): style = ReSTStyle(ReSTDocument()) diff --git a/tests/unit/docs/test_client.py b/tests/unit/docs/test_client.py index 785cc59090..c80cebc6e6 100644 --- a/tests/unit/docs/test_client.py +++ b/tests/unit/docs/test_client.py @@ -71,8 +71,7 @@ def test_document_client(self): self.assert_contains_lines_in_order( [ '.. py:method:: MyService.Client.sample_operation(**kwargs)', - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.sample_operation(', ' Biz=\'string\'', @@ -81,18 +80,15 @@ def test_document_client(self): ' :param Biz:', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Biz\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Biz** *(string) --*', - ' .. raw:: html', - '

Exceptions

', + '**Exceptions**', '* :py:class:`MyService.Client.exceptions.SomeException`', ], self.get_nested_service_contents( @@ -148,13 +144,10 @@ def test_modeled_exceptions(self): self.assert_contains_lines_in_order( [ '.. py:class:: MyService.Client.exceptions.SomeException', - ' .. raw:: html', - '

Example

', - '::', + '**Example**::', 'except client.exceptions.SomeException as e:', '.. py:attribute:: response', - ' .. raw:: html', - '

Syntax

', + '**Syntax**', '{', "'Message': 'string',", "'Error': {", @@ -162,8 +155,7 @@ def test_modeled_exceptions(self): "'Message': 'string'", '}', '}', - ' .. raw:: html', - '

Structure

', + '**Structure**', '- *(dict) --*', '- **Message** *(string) --* ', '- **Error** *(dict) --* ', diff --git a/tests/unit/docs/test_method.py b/tests/unit/docs/test_method.py index 794ba85ed9..a23fb98817 100644 --- a/tests/unit/docs/test_method.py +++ b/tests/unit/docs/test_method.py @@ -142,8 +142,7 @@ def test_default(self): '.. py:method:: foo(**kwargs)', ' This describes the foo method.', cross_ref_link, - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.foo(', ' Bar=\'string\'', @@ -152,14 +151,12 @@ def test_default(self): ' :param Bar:', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Bar\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Bar** *(string) --*', ] @@ -180,8 +177,7 @@ def test_no_input_output_shape(self): [ '.. py:method:: foo()', ' This describes the foo method.', - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.foo()', ' :returns: None', @@ -207,8 +203,7 @@ def test_include_input(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.foo(', ' Bar=\'string\',', @@ -220,14 +215,12 @@ def test_include_input(self): ' :param Biz: biz docs', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Bar\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Bar** *(string) --*', ] @@ -252,8 +245,7 @@ def test_include_output(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.foo(', ' Bar=\'string\'', @@ -262,15 +254,13 @@ def test_include_output(self): ' :param Bar:', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Bar\': \'string\'', ' \'Biz\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Bar** *(string) --*', ' - **Biz** *(string) --*', @@ -292,8 +282,7 @@ def test_exclude_input(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.foo(', ' Biz=\'string\'', @@ -302,15 +291,13 @@ def test_exclude_input(self): ' :param Biz:', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Bar\': \'string\'', ' \'Biz\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Bar** *(string) --*', ' - **Biz** *(string) --*', @@ -335,8 +322,7 @@ def test_exclude_output(self): [ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response = client.foo(', ' Bar=\'string\'', @@ -346,14 +332,12 @@ def test_exclude_output(self): ' :param Biz:', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Biz\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Biz** *(string) --*', ] @@ -463,13 +447,9 @@ def test_deprecated(self): # The line in the example self.assert_contains_lines_in_order( [ - ' .. raw:: html', - '
', - '

Danger

', - ' This operation is deprecated and may not function as ' + ' .. danger::', + ' This operation is deprecated and may not function as ' 'expected. This operation should not be used going forward and is ' 'only kept for the purpose of backwards compatiblity.', - ' .. raw:: html', - '
', ] ) diff --git a/tests/unit/docs/test_paginator.py b/tests/unit/docs/test_paginator.py index 86508ab716..ec420577be 100644 --- a/tests/unit/docs/test_paginator.py +++ b/tests/unit/docs/test_paginator.py @@ -51,8 +51,7 @@ def test_document_paginators(self): ' Creates an iterator that will paginate through responses' ' from :py:meth:`MyService.Client.sample_operation`.' ), - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response_iterator = paginator.paginate(', ' Biz=\'string\',', @@ -75,15 +74,13 @@ def test_document_paginators(self): ' - **StartingToken** *(string) --*', ' :rtype: dict', ' :returns:', - ' .. raw:: html', - '

Response Syntax

', + ' **Response Syntax**', ' ::', ' {', ' \'Biz\': \'string\',', ' \'NextToken\': \'string\'', ' }', - ' .. raw:: html', - '

Response Structure

', + ' **Response Structure**', ' - *(dict) --*', ' - **Biz** *(string) --*', ' - **NextToken** *(string) --*', diff --git a/tests/unit/docs/test_service.py b/tests/unit/docs/test_service.py index 4fdc1b9603..ebdb1d4357 100644 --- a/tests/unit/docs/test_service.py +++ b/tests/unit/docs/test_service.py @@ -78,8 +78,7 @@ def test_document_service(self): self.assert_contains_lines_in_order( [ '.. py:method:: MyService.Client.sample_operation(**kwargs)', - ' .. raw:: html', - '

Examples

', + ' **Examples**', ' Sample Description.', ' ::', ' response = client.sample_operation(', diff --git a/tests/unit/docs/test_sharedexample.py b/tests/unit/docs/test_sharedexample.py index d2e32bb03a..0fe6a773f2 100644 --- a/tests/unit/docs/test_sharedexample.py +++ b/tests/unit/docs/test_sharedexample.py @@ -68,8 +68,7 @@ def test_default(self): ) self.assert_contains_lines_in_order( [ - '.. raw:: html', - '

Examples

', + "**Examples**", "Sample Description.", "::", " response = client.foo(", diff --git a/tests/unit/docs/test_waiter.py b/tests/unit/docs/test_waiter.py index 3ce3aea6ac..f23985e1c9 100644 --- a/tests/unit/docs/test_waiter.py +++ b/tests/unit/docs/test_waiter.py @@ -49,8 +49,7 @@ def test_document_waiters(self): 'every 15 seconds until a successful state is reached. An error ' 'is returned after 40 failed checks.' ), - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' waiter.wait(', ' Biz=\'string\'', diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index ed26ab7b06..eb7a30787b 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -287,8 +287,7 @@ def test_client_method_docstring(self): method_docstring = str(service_client.test_operation.__doc__) ref_docstring_lines = [ 'Documents TestOperation', - '.. raw:: html', - '

Request Syntax

', + '**Request Syntax**', ' response = client.test_operation(', ' Bar=\'string\'', ' Foo=\'string\'', @@ -311,8 +310,7 @@ def test_client_method_help(self): method_docstring = mock_stdout.getvalue() ref_docstring_lines = [ 'Documents TestOperation', - '.. raw:: html', - '

Request Syntax

', + '**Request Syntax**', ' response = client.test_operation(', ' Bar=\'string\'', ' Foo=\'string\'', @@ -1215,8 +1213,7 @@ def test_paginator_help_from_client(self): ' Creates an iterator that will paginate through responses ' 'from :py:meth:`MyService.Client.test_operation`.' ), - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' response_iterator = paginator.paginate(', " Foo='string',", diff --git a/tests/unit/test_waiters.py b/tests/unit/test_waiters.py index 611647572f..62544e180a 100644 --- a/tests/unit/test_waiters.py +++ b/tests/unit/test_waiters.py @@ -771,8 +771,7 @@ def test_waiter_help_documentation(self): 'seconds until a successful state is reached. An error ' 'is returned after 1 failed checks.' ), - ' .. raw:: html', - '

Request Syntax

', + ' **Request Syntax**', ' ::', ' waiter.wait(', " bar='string'", From 9639d4c3214636cf20c8276fa611301db4cd41c5 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Tue, 9 May 2023 10:31:17 -0700 Subject: [PATCH 09/13] Protect against an empty node list. --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8fd7244ef2..5837460f24 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -317,7 +317,7 @@ def visit_strong(self, node): Opens the h3 tag for a specific set of words/phrases and opens the strong tag for all others. """ - if node[0] in self.STRONG_TO_H3_HEADINGS: + if len(node) > 0 and node[0] in self.STRONG_TO_H3_HEADINGS: self.body.append(self.starttag(node, 'h3', '')) else: self.body.append(self.starttag(node, 'strong', '')) From e3ee4b0f12303aadd6f43d9b452d443bd1c2e793 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Tue, 9 May 2023 15:08:36 -0700 Subject: [PATCH 10/13] PR Feedback --- botocore/docs/client.py | 1 + docs/source/conf.py | 52 ++++++++++++++++------------------ tests/unit/docs/test_client.py | 3 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/botocore/docs/client.py b/botocore/docs/client.py index ba2366a846..9559594697 100644 --- a/botocore/docs/client.py +++ b/botocore/docs/client.py @@ -335,6 +335,7 @@ def _add_top_level_documentation(self, section, shape): def _add_exception_catch_example(self, section, shape): section.style.new_line() section.style.bold('Example') + section.style.new_paragraph() section.style.start_codeblock() section.write('try:') section.style.indent() diff --git a/docs/source/conf.py b/docs/source/conf.py index 5837460f24..685989316b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -286,52 +286,50 @@ # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' -class HTML5Translator(SphinxHTML5Translator): - """ - This is our custom HTML5Translator which extends the one provided by Sphinx. - """ +class BotocoreHTML5Translator(SphinxHTML5Translator): + """Extension of Sphinx's ``HTML5Translator`` for Botocore documentation.""" + STRONG_TO_H3_HEADINGS = [ - 'Example', - 'Examples', - 'Exceptions', - 'Request Syntax', - 'Response Structure', - 'Response Syntax', - 'Structure', - 'Syntax' + "Example", + "Examples", + "Exceptions", + "Request Syntax", + "Response Structure", + "Response Syntax", + "Structure", + "Syntax", ] - def visit_admonition(self, node, name=''): - """Uses the h3 tag for admonition titles instead of the p tag""" - self.body.append(self.starttag( - node, 'div', CLASS=('admonition ' + name))) + def visit_admonition(self, node, name=""): + """Uses the h3 tag for admonition titles instead of the p tag.""" + self.body.append(self.starttag(node, "div", CLASS=("admonition " + name))) if name: - title = ( - f"

" - f"{admonitionlabels[name]}

" - ) + title = f"

{admonitionlabels[name]}

" self.body.append(title) def visit_strong(self, node): - """ + """Visit a strong HTML element. + Opens the h3 tag for a specific set of words/phrases and opens the strong tag for all others. """ if len(node) > 0 and node[0] in self.STRONG_TO_H3_HEADINGS: - self.body.append(self.starttag(node, 'h3', '')) + self.body.append(self.starttag(node, "h3", "")) else: - self.body.append(self.starttag(node, 'strong', '')) + self.body.append(self.starttag(node, "strong", "")) def depart_strong(self, node): - """ + """Depart a strong HTML element. + Closes the h3 tag for a specific set of words/phrases and closes the strong tag for all others. """ if node[0] in self.STRONG_TO_H3_HEADINGS: - self.body.append('

') + self.body.append("") else: - self.body.append('') + self.body.append("") + def setup(app): # Register our custom HTML translator. - app.set_translator('html', HTML5Translator) + app.set_translator("html", BotocoreHTML5Translator) diff --git a/tests/unit/docs/test_client.py b/tests/unit/docs/test_client.py index c80cebc6e6..a0ec02a386 100644 --- a/tests/unit/docs/test_client.py +++ b/tests/unit/docs/test_client.py @@ -144,7 +144,8 @@ def test_modeled_exceptions(self): self.assert_contains_lines_in_order( [ '.. py:class:: MyService.Client.exceptions.SomeException', - '**Example**::', + '**Example**', + '::', 'except client.exceptions.SomeException as e:', '.. py:attribute:: response', '**Syntax**', From 76c0306b7a1cedee168e9f7c144263358804c69c Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Wed, 10 May 2023 15:16:16 -0700 Subject: [PATCH 11/13] Make importable. --- botocore/docs/__init__.py | 46 ++++++++++++++++++++++++++++++++++++ docs/source/conf.py | 49 ++------------------------------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/botocore/docs/__init__.py b/botocore/docs/__init__.py index 844f5de59b..ad0dad522c 100644 --- a/botocore/docs/__init__.py +++ b/botocore/docs/__init__.py @@ -13,10 +13,56 @@ import os from botocore.docs.service import ServiceDocumenter +from sphinx.locale import admonitionlabels +from sphinx.writers.html5 import HTML5Translator as SphinxHTML5Translator DEPRECATED_SERVICE_NAMES = {'sms-voice'} +class BotoHTML5Translator(SphinxHTML5Translator): + """Extension of Sphinx's ``HTML5Translator`` for Botocore documentation.""" + + STRONG_TO_H3_HEADINGS = [ + "Example", + "Examples", + "Exceptions", + "Request Syntax", + "Response Structure", + "Response Syntax", + "Structure", + "Syntax", + ] + + def visit_admonition(self, node, name=""): + """Uses the h3 tag for admonition titles instead of the p tag.""" + self.body.append(self.starttag(node, "div", CLASS=("admonition " + name))) + if name: + title = f"

{admonitionlabels[name]}

" + self.body.append(title) + + def visit_strong(self, node): + """Visit a strong HTML element. + + Opens the h3 tag for a specific set of words/phrases and opens the + strong tag for all others. + """ + if len(node) > 0 and node[0] in self.STRONG_TO_H3_HEADINGS: + self.body.append(self.starttag(node, "h3", "")) + else: + self.body.append(self.starttag(node, "strong", "")) + + def depart_strong(self, node): + """Depart a strong HTML element. + + Closes the h3 tag for a specific set of words/phrases and closes the + strong tag for all others. + """ + if node[0] in self.STRONG_TO_H3_HEADINGS: + self.body.append("") + else: + self.body.append("") + + def generate_docs(root_dir, session): """Generates the reference documentation for botocore diff --git a/docs/source/conf.py b/docs/source/conf.py index 685989316b..ebcd8ebab0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,9 +13,7 @@ import datetime, sys, os from botocore.session import get_session -from botocore.docs import generate_docs -from sphinx.locale import admonitionlabels -from sphinx.writers.html5 import HTML5Translator as SphinxHTML5Translator +from botocore.docs import generate_docs, BotoHTML5Translator generate_docs(os.path.dirname(os.path.abspath(__file__)), get_session()) @@ -286,50 +284,7 @@ # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' -class BotocoreHTML5Translator(SphinxHTML5Translator): - """Extension of Sphinx's ``HTML5Translator`` for Botocore documentation.""" - - STRONG_TO_H3_HEADINGS = [ - "Example", - "Examples", - "Exceptions", - "Request Syntax", - "Response Structure", - "Response Syntax", - "Structure", - "Syntax", - ] - - def visit_admonition(self, node, name=""): - """Uses the h3 tag for admonition titles instead of the p tag.""" - self.body.append(self.starttag(node, "div", CLASS=("admonition " + name))) - if name: - title = f"

{admonitionlabels[name]}

" - self.body.append(title) - - def visit_strong(self, node): - """Visit a strong HTML element. - - Opens the h3 tag for a specific set of words/phrases and opens the - strong tag for all others. - """ - if len(node) > 0 and node[0] in self.STRONG_TO_H3_HEADINGS: - self.body.append(self.starttag(node, "h3", "")) - else: - self.body.append(self.starttag(node, "strong", "")) - - def depart_strong(self, node): - """Depart a strong HTML element. - - Closes the h3 tag for a specific set of words/phrases and closes the - strong tag for all others. - """ - if node[0] in self.STRONG_TO_H3_HEADINGS: - self.body.append("") - else: - self.body.append("") - def setup(app): # Register our custom HTML translator. - app.set_translator("html", BotocoreHTML5Translator) + app.set_translator("html", BotoHTML5Translator) From 52a588b01c6e2497a960613c0861152fec608cdd Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Wed, 10 May 2023 15:45:15 -0700 Subject: [PATCH 12/13] Moved class into translator.py --- botocore/docs/__init__.py | 46 ----------------------------- botocore/docs/translator.py | 58 +++++++++++++++++++++++++++++++++++++ docs/source/conf.py | 3 +- 3 files changed, 60 insertions(+), 47 deletions(-) create mode 100644 botocore/docs/translator.py diff --git a/botocore/docs/__init__.py b/botocore/docs/__init__.py index ad0dad522c..844f5de59b 100644 --- a/botocore/docs/__init__.py +++ b/botocore/docs/__init__.py @@ -13,56 +13,10 @@ import os from botocore.docs.service import ServiceDocumenter -from sphinx.locale import admonitionlabels -from sphinx.writers.html5 import HTML5Translator as SphinxHTML5Translator DEPRECATED_SERVICE_NAMES = {'sms-voice'} -class BotoHTML5Translator(SphinxHTML5Translator): - """Extension of Sphinx's ``HTML5Translator`` for Botocore documentation.""" - - STRONG_TO_H3_HEADINGS = [ - "Example", - "Examples", - "Exceptions", - "Request Syntax", - "Response Structure", - "Response Syntax", - "Structure", - "Syntax", - ] - - def visit_admonition(self, node, name=""): - """Uses the h3 tag for admonition titles instead of the p tag.""" - self.body.append(self.starttag(node, "div", CLASS=("admonition " + name))) - if name: - title = f"

{admonitionlabels[name]}

" - self.body.append(title) - - def visit_strong(self, node): - """Visit a strong HTML element. - - Opens the h3 tag for a specific set of words/phrases and opens the - strong tag for all others. - """ - if len(node) > 0 and node[0] in self.STRONG_TO_H3_HEADINGS: - self.body.append(self.starttag(node, "h3", "")) - else: - self.body.append(self.starttag(node, "strong", "")) - - def depart_strong(self, node): - """Depart a strong HTML element. - - Closes the h3 tag for a specific set of words/phrases and closes the - strong tag for all others. - """ - if node[0] in self.STRONG_TO_H3_HEADINGS: - self.body.append("") - else: - self.body.append("") - - def generate_docs(root_dir, session): """Generates the reference documentation for botocore diff --git a/botocore/docs/translator.py b/botocore/docs/translator.py new file mode 100644 index 0000000000..525ee2c2df --- /dev/null +++ b/botocore/docs/translator.py @@ -0,0 +1,58 @@ +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +from sphinx.locale import admonitionlabels +from sphinx.writers.html5 import HTML5Translator as SphinxHTML5Translator + + +class BotoHTML5Translator(SphinxHTML5Translator): + """Extension of Sphinx's ``HTML5Translator`` for Botocore documentation.""" + + STRONG_TO_H3_HEADINGS = [ + "Example", + "Examples", + "Exceptions", + "Request Syntax", + "Response Structure", + "Response Syntax", + "Structure", + "Syntax", + ] + + def visit_admonition(self, node, name=""): + """Uses the h3 tag for admonition titles instead of the p tag.""" + self.body.append(self.starttag(node, "div", CLASS=("admonition " + name))) + if name: + title = f"

{admonitionlabels[name]}

" + self.body.append(title) + + def visit_strong(self, node): + """Visit a strong HTML element. + + Opens the h3 tag for a specific set of words/phrases and opens the + strong tag for all others. + """ + if len(node) > 0 and node[0] in self.STRONG_TO_H3_HEADINGS: + self.body.append(self.starttag(node, "h3", "")) + else: + self.body.append(self.starttag(node, "strong", "")) + + def depart_strong(self, node): + """Depart a strong HTML element. + + Closes the h3 tag for a specific set of words/phrases and closes the + strong tag for all others. + """ + if node[0] in self.STRONG_TO_H3_HEADINGS: + self.body.append("") + else: + self.body.append("") diff --git a/docs/source/conf.py b/docs/source/conf.py index ebcd8ebab0..ca7b6edf8f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,7 +13,8 @@ import datetime, sys, os from botocore.session import get_session -from botocore.docs import generate_docs, BotoHTML5Translator +from botocore.docs import generate_docs +from botocore.docs.translator import BotoHTML5Translator generate_docs(os.path.dirname(os.path.abspath(__file__)), get_session()) From 9e012cb48753fe13a827598134e62708674ce255 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Wed, 10 May 2023 15:53:26 -0700 Subject: [PATCH 13/13] Ran black over new file. --- botocore/docs/translator.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/botocore/docs/translator.py b/botocore/docs/translator.py index 525ee2c2df..aabf36be9a 100644 --- a/botocore/docs/translator.py +++ b/botocore/docs/translator.py @@ -30,9 +30,13 @@ class BotoHTML5Translator(SphinxHTML5Translator): def visit_admonition(self, node, name=""): """Uses the h3 tag for admonition titles instead of the p tag.""" - self.body.append(self.starttag(node, "div", CLASS=("admonition " + name))) + self.body.append( + self.starttag(node, "div", CLASS=("admonition " + name)) + ) if name: - title = f"

{admonitionlabels[name]}

" + title = ( + f"

{admonitionlabels[name]}

" + ) self.body.append(title) def visit_strong(self, node):