docstrfmt 1.5.1.dev0__tar.gz → 1.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/CHANGES.rst +32 -3
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/PKG-INFO +34 -14
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/README.rst +5 -5
- docstrfmt-1.6.0/docstrfmt/const.py +2 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/debug.py +2 -2
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/docstrfmt.py +162 -57
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/main.py +96 -30
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/rst_extras.py +15 -4
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/util.py +10 -4
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt.egg-info/PKG-INFO +34 -14
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt.egg-info/SOURCES.txt +7 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt.egg-info/entry_points.txt +0 -1
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt.egg-info/requires.txt +9 -6
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/pyproject.toml +1 -1
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/setup.py +10 -9
- docstrfmt-1.6.0/tests/test_docstrfmt.py +13 -0
- docstrfmt-1.6.0/tests/test_files/error_files/py_file_error_duplicate_returns.py +12 -0
- docstrfmt-1.6.0/tests/test_files/error_files/py_file_error_duplicate_types.py +11 -0
- docstrfmt-1.6.0/tests/test_files/error_files/py_file_error_multiline_types.py +12 -0
- docstrfmt-1.6.0/tests/test_files/error_files/py_file_type_field_removal.py +16 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/py_file.py +18 -1
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/test_file.rst +28 -0
- docstrfmt-1.6.0/tests/test_main.py +521 -0
- docstrfmt-1.6.0/tests/test_server.py +51 -0
- docstrfmt-1.5.1.dev0/docstrfmt/const.py +0 -2
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/AUTHORS.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/LICENSE.txt +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/MANIFEST.in +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/__init__.py +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/exceptions.py +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt/server.py +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt.egg-info/dependency_links.txt +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/docstrfmt.egg-info/top_level.txt +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/sample.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/setup.cfg +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/bad_pyproject.toml +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/py_file_error_bad_codeblock.py +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/py_file_error_empty_returns.py +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/py_file_error_invalid_rst.py +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/test_invalid_rst_error.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/test_invalid_rst_severe.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/test_invalid_rst_warning.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/test_invalid_syntax.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/error_files/test_invalid_table.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/images/biohazard.png +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/pyproject.toml +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/test_encoding.rst +0 -0
- {docstrfmt-1.5.1.dev0 → docstrfmt-1.6.0}/tests/test_files/test_file.txt +0 -0
|
@@ -1,10 +1,39 @@
|
|
|
1
1
|
Change Log
|
|
2
2
|
==========
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
1.6.0 (2023/12/10)
|
|
5
|
+
------------------
|
|
6
|
+
|
|
7
|
+
**Added**
|
|
8
|
+
|
|
9
|
+
- Added more missing roles.
|
|
10
|
+
- Added support for Python 3.11.
|
|
11
|
+
- Added support for Python 3.12.
|
|
12
|
+
|
|
13
|
+
**Changed**
|
|
14
|
+
|
|
15
|
+
- Improved field sorting and formatting.
|
|
16
|
+
- Improved handling of ``:param:`` and ``:type:`` fields.
|
|
17
|
+
- Bumped ``black``, ``docutils``, ``libcst``, ``platformdirs``, and ``sphinx`` to latest
|
|
18
|
+
versions.
|
|
19
|
+
|
|
20
|
+
**Fixed**
|
|
21
|
+
|
|
22
|
+
- Fix ``:raises:`` field not supporting types.
|
|
23
|
+
|
|
24
|
+
**Removed**
|
|
25
|
+
|
|
26
|
+
- Removed support for Python 3.6.
|
|
27
|
+
- Removed support for Python 3.7.
|
|
28
|
+
|
|
29
|
+
1.5.1 (2022/09/01)
|
|
30
|
+
------------------
|
|
31
|
+
|
|
32
|
+
**Fixed**
|
|
33
|
+
|
|
34
|
+
- Fix ``ImportError`` when importing from black. Pinned black to 22.8.*.
|
|
6
35
|
|
|
7
|
-
1.5.0 (2022/
|
|
36
|
+
1.5.0 (2022/07/19)
|
|
8
37
|
------------------
|
|
9
38
|
|
|
10
39
|
**Added**
|
|
@@ -1,34 +1,56 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: docstrfmt
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: A formatter for Sphinx flavored reStructuredText.
|
|
5
5
|
Home-page: https://github.com/LilSpazJoekp/docstrfmt
|
|
6
6
|
Author: Joel Payne
|
|
7
7
|
Author-email: lilspazjoekp@gmail.com
|
|
8
8
|
License: MIT
|
|
9
|
-
Platform: UNKNOWN
|
|
10
9
|
Classifier: Development Status :: 4 - Beta
|
|
11
10
|
Classifier: Environment :: Console
|
|
12
11
|
Classifier: Intended Audience :: Developers
|
|
13
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
13
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
14
|
Classifier: Programming Language :: Python :: 3.8
|
|
18
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Programming Language :: Python
|
|
21
20
|
Classifier: Topic :: Documentation
|
|
22
21
|
Classifier: Topic :: Documentation :: Sphinx
|
|
23
22
|
Classifier: Topic :: Software Development :: Documentation
|
|
24
23
|
Classifier: Topic :: Utilities
|
|
25
|
-
Requires-Python: ~=3.
|
|
24
|
+
Requires-Python: ~=3.8
|
|
25
|
+
License-File: LICENSE.txt
|
|
26
|
+
License-File: AUTHORS.rst
|
|
27
|
+
Requires-Dist: black==23.*
|
|
28
|
+
Requires-Dist: click==8.*
|
|
29
|
+
Requires-Dist: docutils==0.20.*
|
|
30
|
+
Requires-Dist: libcst==1.*
|
|
31
|
+
Requires-Dist: platformdirs==4.*
|
|
32
|
+
Requires-Dist: sphinx==7.*
|
|
33
|
+
Requires-Dist: tabulate==0.9.*
|
|
34
|
+
Requires-Dist: toml==0.10.*
|
|
35
|
+
Provides-Extra: ci
|
|
36
|
+
Requires-Dist: coveralls; extra == "ci"
|
|
26
37
|
Provides-Extra: d
|
|
38
|
+
Requires-Dist: aiohttp==3.*; extra == "d"
|
|
27
39
|
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: packaging; extra == "dev"
|
|
41
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-aiohttp; extra == "dev"
|
|
44
|
+
Requires-Dist: flake8; extra == "dev"
|
|
45
|
+
Requires-Dist: flynt; extra == "dev"
|
|
46
|
+
Requires-Dist: isort; extra == "dev"
|
|
28
47
|
Provides-Extra: test
|
|
48
|
+
Requires-Dist: pytest; extra == "test"
|
|
49
|
+
Requires-Dist: pytest-aiohttp; extra == "test"
|
|
29
50
|
Provides-Extra: lint
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
Requires-Dist: flake8; extra == "lint"
|
|
52
|
+
Requires-Dist: flynt; extra == "lint"
|
|
53
|
+
Requires-Dist: isort; extra == "lint"
|
|
32
54
|
|
|
33
55
|
docstrfmt: a formatter for Sphinx flavored reStructuredText
|
|
34
56
|
===========================================================
|
|
@@ -167,7 +189,7 @@ Instructions derived from `black documentation
|
|
|
167
189
|
|
|
168
190
|
Note that if you are using a virtual environment detected by PyCharm, this is an
|
|
169
191
|
unneeded step. In this case the path to `docstrfmt` is
|
|
170
|
-
|
|
192
|
+
``$PyInterpreterDirectory$/docstrfmt``.
|
|
171
193
|
|
|
172
194
|
3. Open External tools in PyCharm.
|
|
173
195
|
|
|
@@ -184,7 +206,7 @@ Instructions derived from `black documentation
|
|
|
184
206
|
- Name: docstrfmt
|
|
185
207
|
- Description:
|
|
186
208
|
- Program: <install_location_from_step_2>
|
|
187
|
-
- Arguments:
|
|
209
|
+
- Arguments: ``"$FilePath$"``
|
|
188
210
|
|
|
189
211
|
5. Format the currently opened file by selecting `Tools -> External Tools -> docstrfmt`.
|
|
190
212
|
|
|
@@ -202,9 +224,9 @@ Instructions derived from `black documentation
|
|
|
202
224
|
- File type: Python
|
|
203
225
|
- Scope: Project Files
|
|
204
226
|
- Program: <install_location_from_step_2>
|
|
205
|
-
- Arguments:
|
|
206
|
-
- Output paths to refresh:
|
|
207
|
-
- Working directory:
|
|
227
|
+
- Arguments: ``$FilePath$``
|
|
228
|
+
- Output paths to refresh: ``$FilePath$``
|
|
229
|
+
- Working directory: ``$ProjectFileDir$``
|
|
208
230
|
|
|
209
231
|
3. Uncheck "Auto-save edited files to trigger the watcher" in Advanced Options
|
|
210
232
|
|
|
@@ -236,5 +258,3 @@ With pre-commit
|
|
|
236
258
|
.. _rstfmt: https://github.com/dzhu/rstfmt
|
|
237
259
|
|
|
238
260
|
.. _rustfmt: https://github.com/rust-lang/rustfmt
|
|
239
|
-
|
|
240
|
-
|
|
@@ -135,7 +135,7 @@ Instructions derived from `black documentation
|
|
|
135
135
|
|
|
136
136
|
Note that if you are using a virtual environment detected by PyCharm, this is an
|
|
137
137
|
unneeded step. In this case the path to `docstrfmt` is
|
|
138
|
-
|
|
138
|
+
``$PyInterpreterDirectory$/docstrfmt``.
|
|
139
139
|
|
|
140
140
|
3. Open External tools in PyCharm.
|
|
141
141
|
|
|
@@ -152,7 +152,7 @@ Instructions derived from `black documentation
|
|
|
152
152
|
- Name: docstrfmt
|
|
153
153
|
- Description:
|
|
154
154
|
- Program: <install_location_from_step_2>
|
|
155
|
-
- Arguments:
|
|
155
|
+
- Arguments: ``"$FilePath$"``
|
|
156
156
|
|
|
157
157
|
5. Format the currently opened file by selecting `Tools -> External Tools -> docstrfmt`.
|
|
158
158
|
|
|
@@ -170,9 +170,9 @@ Instructions derived from `black documentation
|
|
|
170
170
|
- File type: Python
|
|
171
171
|
- Scope: Project Files
|
|
172
172
|
- Program: <install_location_from_step_2>
|
|
173
|
-
- Arguments:
|
|
174
|
-
- Output paths to refresh:
|
|
175
|
-
- Working directory:
|
|
173
|
+
- Arguments: ``$FilePath$``
|
|
174
|
+
- Output paths to refresh: ``$FilePath$``
|
|
175
|
+
- Working directory: ``$ProjectFileDir$``
|
|
176
176
|
|
|
177
177
|
3. Uncheck "Auto-save edited files to trigger the watcher" in Advanced Options
|
|
178
178
|
|
|
@@ -16,5 +16,5 @@ def _dump_lines(node: docutils.nodes.Node) -> Iterator[Tuple[int, str]]:
|
|
|
16
16
|
body = str({k: v for k, v in node.attributes.items() if v})
|
|
17
17
|
yield 0, f"{head} {body}"
|
|
18
18
|
for c in node.children:
|
|
19
|
-
for n,
|
|
20
|
-
yield n + 1,
|
|
19
|
+
for n, line in _dump_lines(c):
|
|
20
|
+
yield n + 1, line
|
|
@@ -126,14 +126,12 @@ class CodeFormatters:
|
|
|
126
126
|
compile(code, context.current_file, mode="exec")
|
|
127
127
|
except SyntaxError as syntax_error:
|
|
128
128
|
context.manager.error_count += 1
|
|
129
|
-
|
|
130
|
-
source = f.read()
|
|
131
|
-
current_line = get_code_line(source, code)
|
|
129
|
+
current_line = get_code_line(context.current_file, code, strict=True)
|
|
132
130
|
if context.manager.reporter:
|
|
133
131
|
context.manager.reporter.error(
|
|
134
132
|
f"SyntaxError: {syntax_error.msg}:\n\nFile"
|
|
135
133
|
f' "{context.current_file}", line'
|
|
136
|
-
f' {current_line}:\n{syntax_error.text}{" " * (syntax_error.offset-1)}^'
|
|
134
|
+
f' {current_line}:\n{syntax_error.text}{" " * (syntax_error.offset - 1)}^'
|
|
137
135
|
)
|
|
138
136
|
return code
|
|
139
137
|
|
|
@@ -422,8 +420,8 @@ class Formatters:
|
|
|
422
420
|
sub_doc = self.manager.parse_string(
|
|
423
421
|
context.current_file, "\n".join(directive.content)
|
|
424
422
|
)
|
|
425
|
-
if sub_doc.children:
|
|
426
|
-
yield ""
|
|
423
|
+
if sub_doc.children:
|
|
424
|
+
yield ""
|
|
427
425
|
yield from self._with_spaces(
|
|
428
426
|
4, self.manager.perform_format(sub_doc, context.indent(4))
|
|
429
427
|
)
|
|
@@ -456,20 +454,19 @@ class Formatters:
|
|
|
456
454
|
try:
|
|
457
455
|
first_line = next(children)
|
|
458
456
|
except StopIteration:
|
|
459
|
-
with open(context.current_file, encoding="utf-8") as f:
|
|
460
|
-
source = f.read()
|
|
461
|
-
line = get_code_line(source, f":{node.astext().strip()}:")
|
|
462
457
|
raise InvalidRstError(
|
|
463
458
|
context.current_file,
|
|
464
459
|
"ERROR",
|
|
465
|
-
|
|
460
|
+
get_code_line(
|
|
461
|
+
context.current_file, f":{node.astext().strip()}:", strict=True
|
|
462
|
+
),
|
|
466
463
|
f"Empty `:{node.astext().strip()}:` field. Please add a field body or"
|
|
467
|
-
" omit completely",
|
|
464
|
+
" omit completely.",
|
|
468
465
|
)
|
|
469
466
|
children = list(children)
|
|
470
467
|
children_processed = []
|
|
471
468
|
for i, child in enumerate(children):
|
|
472
|
-
if child.startswith(
|
|
469
|
+
if child.startswith(".."):
|
|
473
470
|
blocks_in_child = [child]
|
|
474
471
|
for block in children[i + 1 :]:
|
|
475
472
|
if block.startswith(" "):
|
|
@@ -485,11 +482,6 @@ class Formatters:
|
|
|
485
482
|
else:
|
|
486
483
|
children_processed.append(child)
|
|
487
484
|
children = children_processed
|
|
488
|
-
if field_name in [":raises:", ":returns:"]:
|
|
489
|
-
if node.parent.index(node):
|
|
490
|
-
previous = node.parent.children[node.parent.children.index(node) - 1]
|
|
491
|
-
if previous.tagname == "field":
|
|
492
|
-
yield ""
|
|
493
485
|
yield f"{field_name} {first_line}"
|
|
494
486
|
yield from self._with_spaces(4, children)
|
|
495
487
|
|
|
@@ -505,16 +497,119 @@ class Formatters:
|
|
|
505
497
|
)
|
|
506
498
|
|
|
507
499
|
def field_list(self, node: docutils.nodes.field_list, context) -> line_iterator:
|
|
508
|
-
|
|
500
|
+
param_types = {}
|
|
501
|
+
param_fields = []
|
|
502
|
+
returns_fields = []
|
|
503
|
+
rtype_fields = []
|
|
504
|
+
raises_fields = []
|
|
505
|
+
other_fields = []
|
|
506
|
+
field_types_mapping = {
|
|
507
|
+
"param": param_fields,
|
|
508
|
+
"arg": param_fields,
|
|
509
|
+
"argument": param_fields,
|
|
510
|
+
"return": returns_fields,
|
|
511
|
+
"returns": returns_fields,
|
|
512
|
+
"rtype": rtype_fields,
|
|
513
|
+
"raise": raises_fields,
|
|
514
|
+
"raises": raises_fields,
|
|
515
|
+
}
|
|
516
|
+
already_typed = []
|
|
517
|
+
children = node.children
|
|
518
|
+
for child in children[:]:
|
|
519
|
+
field_body = child.children[0].children[0]
|
|
520
|
+
field_typing = None
|
|
521
|
+
try:
|
|
522
|
+
field_kind, *field_typing, field_name = field_body.split(" ")
|
|
523
|
+
except ValueError:
|
|
524
|
+
field_name = None
|
|
525
|
+
field_kind = field_body
|
|
526
|
+
child.children[0].setdefault("name", field_name)
|
|
527
|
+
if field_kind == "type":
|
|
528
|
+
field_type = child.children[1].children[0].astext()
|
|
529
|
+
if "\n" in field_type:
|
|
530
|
+
raise InvalidRstError(
|
|
531
|
+
context.current_file,
|
|
532
|
+
"ERROR",
|
|
533
|
+
get_code_line(context.current_file, field_type),
|
|
534
|
+
"Multi-line type hints are not supported.",
|
|
535
|
+
)
|
|
536
|
+
param_types[field_name] = field_type
|
|
537
|
+
node.remove(child)
|
|
538
|
+
continue
|
|
539
|
+
if field_typing:
|
|
540
|
+
already_typed.append(field_name)
|
|
541
|
+
if field_kind in field_types_mapping:
|
|
542
|
+
if field_kind.startswith("return") and returns_fields:
|
|
543
|
+
raise InvalidRstError(
|
|
544
|
+
context.current_file,
|
|
545
|
+
"ERROR",
|
|
546
|
+
get_code_line(context.current_file, child.astext()),
|
|
547
|
+
"Multiple `:return:` fields are not allowed. Please"
|
|
548
|
+
" combine them into one.",
|
|
549
|
+
)
|
|
550
|
+
field_types_mapping[field_kind].append(child)
|
|
551
|
+
else:
|
|
552
|
+
other_fields.append(child)
|
|
553
|
+
for field in param_fields:
|
|
554
|
+
field_name = field.children[0].get("name")
|
|
555
|
+
if field_name in already_typed and field_name in param_types:
|
|
556
|
+
raise InvalidRstError(
|
|
557
|
+
context.current_file,
|
|
558
|
+
"ERROR",
|
|
559
|
+
get_code_line(context.current_file, field.astext()),
|
|
560
|
+
"Type hint is specified both in the field body and in the"
|
|
561
|
+
" `:type:` field. Please remove one of them.",
|
|
562
|
+
)
|
|
563
|
+
else:
|
|
564
|
+
field_typing = param_types.get(field_name, None)
|
|
565
|
+
if field_typing:
|
|
566
|
+
field.children[0].replace_self(
|
|
567
|
+
docutils.nodes.field_name(
|
|
568
|
+
"", f"param {field_typing} {field_name}"
|
|
569
|
+
)
|
|
570
|
+
)
|
|
571
|
+
yield from chain(
|
|
572
|
+
self.manager.perform_format(child, context) for child in param_fields
|
|
573
|
+
)
|
|
574
|
+
yield from self._prepend_if_any(
|
|
575
|
+
"",
|
|
576
|
+
chain(
|
|
577
|
+
self.manager.perform_format(child, context)
|
|
578
|
+
for child in returns_fields + rtype_fields
|
|
579
|
+
),
|
|
580
|
+
)
|
|
581
|
+
yield from self._prepend_if_any(
|
|
582
|
+
"",
|
|
583
|
+
chain(
|
|
584
|
+
self.manager.perform_format(child, context) for child in raises_fields
|
|
585
|
+
),
|
|
586
|
+
)
|
|
587
|
+
if (
|
|
588
|
+
other_fields
|
|
589
|
+
and param_fields + returns_fields + rtype_fields + raises_fields
|
|
590
|
+
):
|
|
591
|
+
yield ""
|
|
592
|
+
yield from chain(
|
|
593
|
+
self.manager.perform_format(child, context) for child in other_fields
|
|
594
|
+
)
|
|
509
595
|
|
|
510
596
|
def field_name(self, node: docutils.nodes.field_name, context) -> line_iterator:
|
|
511
597
|
text = " ".join(chain(self._format_children(node, context)))
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
598
|
+
body = ":"
|
|
599
|
+
field_kinds = [
|
|
600
|
+
("param", "arg"),
|
|
601
|
+
"raise",
|
|
602
|
+
"return",
|
|
603
|
+
]
|
|
604
|
+
for field_kind in field_kinds:
|
|
605
|
+
if text.startswith(field_kind):
|
|
606
|
+
field_kind, *_ = text.split(" ", maxsplit=1)
|
|
607
|
+
body += field_kind
|
|
608
|
+
text = text[len(field_kind) :]
|
|
609
|
+
break
|
|
610
|
+
body += text
|
|
611
|
+
body += ":"
|
|
612
|
+
yield body
|
|
518
613
|
|
|
519
614
|
def footnote(self, node: docutils.nodes.footnote_reference, context):
|
|
520
615
|
prefix = ".."
|
|
@@ -545,7 +640,7 @@ class Formatters:
|
|
|
545
640
|
|
|
546
641
|
indent = 4 * context.line_block_depth
|
|
547
642
|
context = context.indent(indent)
|
|
548
|
-
prefix1 = "|
|
|
643
|
+
prefix1 = f"|{' ' * (indent - 1)}"
|
|
549
644
|
prefix2 = " " * indent
|
|
550
645
|
for first, line in self._enum_first(
|
|
551
646
|
self._wrap_text(
|
|
@@ -568,7 +663,7 @@ class Formatters:
|
|
|
568
663
|
)
|
|
569
664
|
context.current_ordinal += 1
|
|
570
665
|
width = len(context.bullet) + 1
|
|
571
|
-
bullet = context.bullet
|
|
666
|
+
bullet = f"{context.bullet} "
|
|
572
667
|
spaces = " " * width
|
|
573
668
|
context = context.indent(width)
|
|
574
669
|
context.bullet = ""
|
|
@@ -658,40 +753,42 @@ class Formatters:
|
|
|
658
753
|
yield inline_markup(title + anonymous_suffix(anonymous))
|
|
659
754
|
return
|
|
660
755
|
|
|
661
|
-
# Handle references to external URIs. They can be either standalone hyperlinks,
|
|
662
|
-
# just the URI, or an explicit "`text <url>`_" or "`text <url>`__".
|
|
756
|
+
# Handle references to external URIs. They can be either standalone hyperlinks,
|
|
757
|
+
# written as just the URI, or an explicit "`text <url>`_" or "`text <url>`__".
|
|
663
758
|
if "refuri" in attributes:
|
|
664
759
|
uri = attributes["refuri"]
|
|
665
|
-
if uri == title or uri == "mailto:"
|
|
760
|
+
if uri == title or uri == f"mailto:{title}":
|
|
666
761
|
yield inline_markup(title)
|
|
667
762
|
else:
|
|
668
763
|
anonymous = "target" not in attributes
|
|
669
764
|
yield inline_markup(f"`{title} <{uri}>`{anonymous_suffix(anonymous)}")
|
|
670
765
|
return
|
|
671
766
|
|
|
672
|
-
# Simple reference names can consist of "alphanumerics plus isolated (no two
|
|
673
|
-
# internal hyphens, underscores, periods, colons and plus signs",
|
|
767
|
+
# Simple reference names can consist of "alphanumerics plus isolated (no two
|
|
768
|
+
# adjacent) internal hyphens, underscores, periods, colons and plus signs",
|
|
769
|
+
# according to
|
|
674
770
|
# https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names.
|
|
675
771
|
is_single_word = re.match("^[-_.:+a-zA-Z0-9]+$", title) and not re.search(
|
|
676
772
|
"[-_.:+][-_.:+]", title
|
|
677
773
|
)
|
|
678
774
|
|
|
679
|
-
# "x__" is one of the few cases to trigger an explicit "anonymous" attribute
|
|
680
|
-
# being the similar "|x|__", which is already handled above).
|
|
775
|
+
# "x__" is one of the few cases to trigger an explicit "anonymous" attribute
|
|
776
|
+
# (the other being the similar "|x|__", which is already handled above).
|
|
681
777
|
if "anonymous" in attributes:
|
|
682
778
|
if not is_single_word:
|
|
683
|
-
title = "`
|
|
779
|
+
title = f"`{title}`"
|
|
684
780
|
yield inline_markup(title + anonymous_suffix(True))
|
|
685
781
|
return
|
|
686
782
|
|
|
687
783
|
anonymous = "target" not in attributes
|
|
688
784
|
ref = attributes["refname"]
|
|
689
|
-
# Check whether the reference name matches the text and can be made implicit.
|
|
690
|
-
# names are case-insensitive.)
|
|
785
|
+
# Check whether the reference name matches the text and can be made implicit.
|
|
786
|
+
# (Reference names are case-insensitive.)
|
|
691
787
|
if anonymous and ref.lower() == title.lower():
|
|
692
788
|
if not is_single_word:
|
|
693
|
-
title = "`
|
|
694
|
-
# "x_" is equivalent to "`x <x_>`__"; it's anonymous despite having a single
|
|
789
|
+
title = f"`{title}`"
|
|
790
|
+
# "x_" is equivalent to "`x <x_>`__"; it's anonymous despite having a single
|
|
791
|
+
# underscore.
|
|
695
792
|
yield inline_markup(title + anonymous_suffix(False))
|
|
696
793
|
else:
|
|
697
794
|
yield inline_markup(f"`{title} <{ref}_>`{anonymous_suffix(anonymous)}")
|
|
@@ -748,9 +845,9 @@ class Formatters:
|
|
|
748
845
|
|
|
749
846
|
def table(self, node: docutils.nodes.table, context) -> line_iterator:
|
|
750
847
|
rows = []
|
|
751
|
-
for row in node.
|
|
848
|
+
for row in node.findall(docutils.nodes.row):
|
|
752
849
|
current_row = []
|
|
753
|
-
for column in row.
|
|
850
|
+
for column in row.findall(docutils.nodes.entry):
|
|
754
851
|
if column.attributes.get("morerows", False) or column.attributes.get(
|
|
755
852
|
"morecols", False
|
|
756
853
|
):
|
|
@@ -779,7 +876,7 @@ class Formatters:
|
|
|
779
876
|
final_widths = [
|
|
780
877
|
max(
|
|
781
878
|
self._generate_table_matrix(context, rows, None, max_col_len),
|
|
782
|
-
key=lambda
|
|
879
|
+
key=lambda lengths: lengths[i],
|
|
783
880
|
)[i]
|
|
784
881
|
for i in range(column_count)
|
|
785
882
|
]
|
|
@@ -811,7 +908,7 @@ class Formatters:
|
|
|
811
908
|
final_widths = [
|
|
812
909
|
max(
|
|
813
910
|
self._generate_table_matrix(context, rows, None, column_lengths),
|
|
814
|
-
key=lambda
|
|
911
|
+
key=lambda lengths: lengths[i],
|
|
815
912
|
)[i]
|
|
816
913
|
for i in range(column_count)
|
|
817
914
|
]
|
|
@@ -831,7 +928,11 @@ class Formatters:
|
|
|
831
928
|
try:
|
|
832
929
|
body = f" {node.attributes['refuri']}"
|
|
833
930
|
except KeyError:
|
|
834
|
-
body =
|
|
931
|
+
body = (
|
|
932
|
+
f" {node.attributes['refname']}_"
|
|
933
|
+
if "refname" in node.attributes
|
|
934
|
+
else ""
|
|
935
|
+
)
|
|
835
936
|
|
|
836
937
|
name = "_" if node.attributes.get("anonymous") else node.attributes["names"][0]
|
|
837
938
|
yield f".. _{name}:{body}"
|
|
@@ -922,7 +1023,13 @@ class Manager:
|
|
|
922
1023
|
self.docstring_trailing_line = docstring_trailing_line
|
|
923
1024
|
|
|
924
1025
|
def _pre_process(self, node: docutils.nodes.Node, source: str) -> None:
|
|
925
|
-
"""
|
|
1026
|
+
"""Preprocess nodes.
|
|
1027
|
+
|
|
1028
|
+
This does some preprocessing to all nodes that is generic across node types and
|
|
1029
|
+
is therefore most convenient to do as a simple recursive function rather than as
|
|
1030
|
+
part of the big dispatcher class.
|
|
1031
|
+
|
|
1032
|
+
"""
|
|
926
1033
|
# Strip all system_message nodes. (Just formatting them with no markup isn't enough, since that
|
|
927
1034
|
# could lead to extra spaces or empty lines between other elements.)
|
|
928
1035
|
errors = [
|
|
@@ -975,21 +1082,19 @@ class Manager:
|
|
|
975
1082
|
self._pre_process(child, source)
|
|
976
1083
|
|
|
977
1084
|
def format_node(self, width, node: docutils.nodes.Node, is_docstring=False) -> str:
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
),
|
|
989
|
-
)
|
|
1085
|
+
formatted_node = "\n".join(
|
|
1086
|
+
self.perform_format(
|
|
1087
|
+
node,
|
|
1088
|
+
FormatContext(
|
|
1089
|
+
width,
|
|
1090
|
+
current_file=self.current_file,
|
|
1091
|
+
manager=self,
|
|
1092
|
+
black_config=self.black_config,
|
|
1093
|
+
is_docstring=is_docstring,
|
|
1094
|
+
),
|
|
990
1095
|
)
|
|
991
|
-
+ "\n"
|
|
992
1096
|
)
|
|
1097
|
+
return f"{formatted_node}\n"
|
|
993
1098
|
|
|
994
1099
|
def parse_string(self, file_name: str, text: str) -> docutils.nodes.document:
|
|
995
1100
|
self.current_file = file_name
|