- Avoid representing internal data as dictionaries if possible.
- Would be nice to support output to string, stdout or a file through wrappers.
StringIO makes this easy.
- Avoid
\t - it has medium-dependent indentation; better to have space rendering with a user-configurable indentation level.
- You should wrap everything, not just your description.
- This is a matter of opinion, but I disagree with double-newlines on the inside of your sections; reserving double-newlines for section breaks makes things clearer.
Example code
from dataclasses import dataclass
from io import StringIO
from sys import stdout
from textwrap import TextWrapper
from typing import Optional, Tuple, Sequence, TextIO, Iterable
Pair = Tuple[str, str]
PairSeq = Sequence[Pair]
@dataclass
class Documentation:
summary: Optional[str] = None
args: Optional[PairSeq] = None
attrs: Optional[PairSeq] = None
raises: Optional[PairSeq] = None
returns: Optional[str] = None
desc: Optional[str] = None
todo: Optional[Sequence[str]] = None
indent: int = 4
wrap: int = 80
def __post_init__(self):
self._outer_wrapper = TextWrapper(width=self.wrap).wrap
indent = ' ' * self.indent
self._inner_wrapper = TextWrapper(
width=self.wrap,
initial_indent=indent,
subsequent_indent=indent,
).wrap
def inner_wrap(self, t: str) -> Iterable[str]:
for line in self._inner_wrapper(t):
yield line + '\n'
def outer_wrap(self, t: str) -> Iterable[str]:
for line in self._outer_wrapper(t):
yield line + '\n'
def __str__(self) -> str:
with StringIO() as f:
self.to_file(f)
return f.getvalue()
def print(self) -> None:
self.to_file(stdout)
def write_section(self, title: str, content: str, f: TextIO) -> None:
if content:
f.write(f'\n{title}:\n')
f.writelines(self.inner_wrap(content))
def write_pairs(self, title: str, pairs: PairSeq, f: TextIO) -> None:
if pairs:
f.write(f'\n{title}:\n')
f.writelines(
line
for name, desc in pairs
for line in self.inner_wrap(f'{name}: {desc}')
)
def to_file(self, f: TextIO) -> None:
if self.summary:
f.writelines(self.outer_wrap(self.summary))
self.write_pairs('Arguments', self.args, f)
self.write_pairs('Attributes', self.attrs, f)
self.write_pairs('Raises', self.raises, f)
self.write_section('Returns', self.returns, f)
self.write_section('Description', self.desc, f)
if self.todo:
f.write(f'\nTodo:\n')
f.writelines(
line
for todo in self.todo
for line in self.inner_wrap(f'* {todo}')
)
def test():
Documentation(
summary='The Pear object describes the properties of pears.',
args=(
('a', 'Function argument'),
('b', 'Function argument'),
),
attrs=(
('a', 'Information about parameter a'),
('b', 'Information about parameter b'),
),
raises=(
('AttributeError', 'The ``Raises`` section is a list of all '
'exceptions that are relevant to the interface.'),
),
returns='The return value. True for success, False otherwise.',
desc='The description may span multiple lines. Following lines should be indented. The type is optional. The description may '
'span multiple lines. Following lines should be indented. The type is optional.The description may span multiple lines. '
'Following lines should be indented. The type is optional.The description may span multiple lines. Following lines '
'should be indented. The type is optional.',
todo=('Do Something', 'Something else'),
).print()
if __name__ == '__main__':
test()
Output
The Pear object describes the properties of pears.
Arguments:
a: Function argument
b: Function argument
Attributes:
a: Information about parameter a
b: Information about parameter b
Raises:
AttributeError: The ``Raises`` section is a list of all exceptions that are
relevant to the interface.
Returns:
The return value. True for success, False otherwise.
Description:
The description may span multiple lines. Following lines should be indented.
The type is optional. The description may span multiple lines. Following
lines should be indented. The type is optional.The description may span
multiple lines. Following lines should be indented. The type is optional.The
description may span multiple lines. Following lines should be indented. The
type is optional.
Todo:
* Do Something
* Something else