Docstring generator in Google type Hello everyone!
In this task I had to create docstring generator. I'm just wondering how you see this problem, I have to &quot;take data&quot; from the dictionary
{'Args': None, 'Returns': None, 'Raises': None, 'Attributes': None, 'Summary': None, 'Description': None}

and then generate docstring in the correct order.
This is my solution:
import textwrap

def gendoc(config):
    docstring = []
    if (&quot;Summary&quot; in config) and (config[&quot;Summary&quot;] is not None):
        docstring.append(config[&quot;Summary&quot;])
    if (&quot;Args&quot; in config) and (config[&quot;Args&quot;] is not None):
        args = [&quot;Args:&quot;] + [f&quot;\t{arg}: Function argument&quot; for arg in config[&quot;Args&quot;]]
        docstring += args
    if (&quot;Attributes&quot; in config) and (config[&quot;Attributes&quot;] is not None):
        attr = [&quot;Attributes:&quot;] + [f&quot;\t{attr}: Information about parameter {attr}&quot; for attr in config[&quot;Attributes&quot;]]
        docstring += attr
    if (&quot;Raises&quot; in config) and (config[&quot;Raises&quot;] is not None):
        raises = [&quot;Raises: &quot;] + [f&quot;\t {config.get('Raises')}&quot;]
        docstring += raises
    if (&quot;Returns&quot; in config) and (config[&quot;Returns&quot;] is not None):
        returns = [&quot;Returns: &quot;] + [f&quot;\t {config.get('Returns')}&quot;]
        docstring += returns
    if (&quot;Description&quot; in config) and (config[&quot;Description&quot;] is not None):
        value = config.get('Description')
        wrapper=textwrap.TextWrapper(subsequent_indent= '\t', width = 120)
        description = [&quot;Description: &quot;] + [f&quot;\t{wrapper.fill(value)}&quot;]
        docstring += description
    if (&quot;Todo&quot; in config) and (config[&quot;Todo&quot;] is not None):
        star = &quot;*&quot;
        todo = [&quot;Todo:&quot;] + [f&quot;\t{star} {todo}&quot; for todo in config[&quot;Todo&quot;]]
        docstring += todo
        
    docstring = &quot;\n\n&quot;.join(docstring)
    return docstring


My output:
The Pear object describes the properties of pears.

Args:

    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
 

Any tips how to make this SHORTER and more neat, closer to a advanced or just better solution will be definitely on point. Thanks in advance!
Have a great day!
if (&quot;Summary&quot; in config) and (config[&quot;Summary&quot;] is not None):


Can be simplified to:
if config.get(&quot;summary&quot;) is not None:




args = [&quot;Args:&quot;] + [f&quot;\t{arg}: Function argument&quot; for arg in config[&quot;Args&quot;]]
attr = [&quot;Attributes:&quot;] + [f&quot;\t{attr}: Information about parameter {attr}&quot; for attr in config[&quot;Attributes&quot;]]
todo = [&quot;Todo:&quot;] + [f&quot;\t{star} {todo}&quot; for todo in config[&quot;Todo&quot;]]


Can all be moved into a function.

You always add f&quot;{Name}:&quot;,
You always iterate through config[Name], and
You always format the value. Which can be provided as a format string (not an f-string).

def format_values(config, name, fmt):
    return [f&quot;{name}:&quot;] + [fmt.format(value) for value in config[name]]

format_values(config, &quot;Args&quot;, &quot;\t{}: Function argument&quot;)




docstring.append(config[&quot;Summary&quot;])
raises = [&quot;Raises: &quot;] + [f&quot;\t {config.get('Raises')}&quot;]
returns = [&quot;Returns: &quot;] + [f&quot;\t {config.get('Returns')}&quot;]


Can all be moved into a function.

You add f&quot;{Name}: &quot; except for Summary,
You always add config[Name], and
You always format the value. (Summary would be &quot;{}&quot;)

def format_value(config, name, fmt, head=True):
    return ([f&quot;{name}: &quot;] if head else []) + [fmt.format(config[name])]

format_value(config, &quot;Summary&quot;, &quot;{}&quot;, False)




value = config.get('Description')
wrapper=textwrap.TextWrapper(subsequent_indent= '\t', width = 120)
description = [&quot;Description: &quot;] + [f&quot;\t{wrapper.fill(value)}&quot;]
docstring += description


To be able to handle Description we can change format_value (and optionally format_values for consistency) to take a lambda to decide how to display the value.
def format_value(config, name, fmt, head=True):
    return ([f&quot;{name}: &quot;] if head else []) + [fmt(config[name])]

wrapper = textwrap.TextWrapper(subsequent_indent='\t', width=120)
docstring += format_value(config, &quot;Description&quot;, lambda value: f&quot;\t{wrapper.fill(value)}&quot;)



Lets look at how the code looks now:
import textwrap


def format_value(config, name, fmt, head=True):
    return ([f&quot;{name}: &quot;] if head else []) + [fmt(config[name])]


def format_values(config, name, fmt):
    return [f&quot;{name}:&quot;] + [fmt(value) for value in config[name]]


def gendoc(config):
    docstring = []
    if None is not config.get(&quot;Summary&quot;):
        docstring += format_value(config, &quot;Summary&quot;, lambda summary: summary, False)
    if None is not config.get(&quot;Args&quot;):
        docstring += format_values(config, &quot;Args&quot;, lambda arg: f&quot;\t{arg}: Function argument&quot;)
    if None is not config.get(&quot;Attributes&quot;):
        docstring += format_values(config, &quot;Attributes&quot;, lambda attr: f&quot;\t{arg}: Information about parameter {arg}&quot;)
    if None is not config.get(&quot;Raises&quot;):
        docstring += format_value(config, &quot;Raises&quot;, lambda raises: f&quot;\t {raises}&quot;)
    if None is not config.get(&quot;Returns&quot;):
        docstring += format_value(config, &quot;Returns&quot;, lambda returns: f&quot;\t {returns}&quot;)
    if None is not config.get(&quot;Description&quot;):
        wrapper = textwrap.TextWrapper(subsequent_indent='\t', width=120)
        docstring += format_value(config, &quot;Description&quot;, lambda value: f&quot;\t{wrapper.fill(value)}&quot;)
    if None is not config.get(&quot;Todo&quot;):
        star = &quot;*&quot;
        docstring += format_values(config, &quot;Todo&quot;, lambda todo: f&quot;\t{star} {todo}&quot;)

    docstring = &quot;\n\n&quot;.join(docstring)
    return docstring


if None is not config.get({name}):
    docstring += format_value(config, {name}, ...)


We can see we are duplicating the above pseudocode.
Lets ignore the fmt and head arguments to format_value and format_values.
We could change the code to:
docstring = []
for name, fn in ...:
    if config.get(name) is not None:
        docstring += fn(config, name)

We can change format_value and format_values so we provide the fmt and head.
We then return another function which takes config and name to call in the loop.
def format_value(fmt, head=True):
    def inner(config, name):
        return ([f&quot;{name}: &quot;] if head else []) + [fmt(config[name])]
    return inner

format_value(&quot;{}&quot;)(config, &quot;Summary&quot;)

Next we can define a dictionary to store the result of the first call to format_value and format_values.
And then change the code to iterate over the dictionary.
Final code:
import textwrap


def format_value(fmt, head=True):
    def inner(config, name):
        return ([f&quot;{name}: &quot;] if head else []) + [fmt(config[name])]
    return inner


def format_values(fmt):
    def inner(config, name):
        return [f&quot;{name}:&quot;] + [fmt(value) for value in config[name]]
    return inner


wrapper = textwrap.TextWrapper(subsequent_indent='\t', width=120)
star = &quot;*&quot;

FORMATS = {
    &quot;Summary&quot;: format_value(lambda summary: f&quot;{summary}&quot;, False),
    &quot;Args&quot;: format_values(lambda arg: f&quot;\t{arg}: Function argument&quot;),
    &quot;Attributes&quot;: format_values(lambda attr: f&quot;\t{arg}: Information about parameter {arg}&quot;),
    &quot;Raises&quot;: format_value(lambda raises: f&quot;\t {raises}&quot;),
    &quot;Returns&quot;: format_value(lambda returns: f&quot;\t {returns}&quot;),
    &quot;Description&quot;: format_value(lambda value: f&quot;\t{wrapper.fill(value)}&quot;),
    &quot;Todo&quot;: format_values(lambda todo: f&quot;\t{star} {todo}&quot;),
}


def gendoc(config):
    docstring = []
    for name, fn in FORMATS.items():
        if config.get(name) is not None:
            docstring += fn(config, name)
    return &quot;\n\n&quot;.join(docstring)