Formatting Python docstrings for readability when printed - python

I've just started getting into adding docstrings to my classes/methods and I'm having difficulty in formatting them such that they are easily readable when printed. Some of the lines within the docstrings are long enough to wrap around on my IDE, and when I print the docstring in the console there are large gaps of whitespace in these breaks. Additionally, I would like to maintain a consistent indent scheme throughout the docstring, but these linebreaks violate it by forcing lines to print with not indent.
Are there certain best practices to docstring writing that I'm ignoring? Are there ways to print large strings such that formatting is respected?
Hope this makes sense, thanks.

Normally you use the help utility for viewing docstrings, it deals with inconsistency in whitespace:
>>> def test():
""" first line
HELLO THERE#
ME TOO
DOC STRING HERE
"""
return 1
>>> help(test)
Help on function test in module __main__:
test()
first line
HELLO THERE#
ME TOO
DOC STRING HERE
>>> def test2():
"""
DOC string
text here"""
return 5
>>> help(test2)
Help on function test2 in module __main__:
test2()
DOC string
text here
So while you can refer to PEP 8 for usual conventions, you can also just decide what format you like and just try to be consistent within your application.

Related

Can a Python docstring be calculated (f-string or %-expression)?

Is it possible to have a Python docstring calculated? I have a lot of repetitive things in my docstrings, so I'd like to either use f-strings or a %-style format expression.
When I use an f-string at the place of a docstring
importing the module invokes the processing
but when I check the __doc__ of such a function it is empty
sphinx barfs when the docstring is an f-string
I do know how to process the docstrings after the import, but that doesn't work for object 'doc' strings which is recognized by sphinx but is not a real __doc__'s of the object.
Docstrings in Python must be regular string literals.
This is pretty easy to test - the following program does not show the docstring:
BAR = "Hello world!"
def foo():
f"""This is {BAR}"""
pass
assert foo.__doc__ is None
help(foo)
The Python syntax docs say that the docstring must be a "string literal", and the tail end of the f-string reference says they "cannot be used as docstrings".
So unfortunately you must use the __doc__ attribute.
However, you should be able to use a decorator to read the __doc__ attribute and replace it with whatever you want.

Python doctest how to match single or double quote in output as opposed to only single quote

I commonly use the code auto-formatter Black, which has trained me to use double quotes.
It has become "muscle memory" for me to use double quotes.
The default repr for many classes prints from the Python interactive output with single quotes. I know doctest seeks to reproduce the Python interactive output.
Is there some simple way to have doctest match single or double quotes?
I know one option is to make a custom doctest.OutputChecker. I am wondering if there's a simpler option.
Code Sample
from dataclasses import dataclass
#dataclass
class SomeDataClass:
"""Contains a string."""
data: str
def get_some_dataclass(data: str) -> SomeDataClass:
"""Get a data class containing some specified data.
Returns:
Data class containing the data.
Examples:
>>> get_some_dataclass("hi")
SomeDataClass(data="hi")
"""
return SomeDataClass(data=data)
if __name__ == "__main__":
import doctest
doctest.testmod()
Output:
**********************************************************************
File "quote_or_apostrophe.py", line 23, in __main__.get_some_dataclass
Failed example:
get_some_dataclass("hi")
Expected:
SomeDataClass(data="hi")
Got:
SomeDataClass(data='hi')
**********************************************************************
Versions
I invoke doctest via pytest's doctest integration.
python==3.8.2
pytest==5.4.1
I imagine you could achieve this with blacken-docs but I haven't seen an example of this done yet...
Either the approach of:
Leave the strings in black standard (double quoted) and find a way to transform the expected output doctest so that it has double quotes (by running black on it somehow?)
Run blacken-docs with a single quote setting applied, which also avoids having to think about it yourself
I would instinctively go for the first option because it feels like it solves the problem better, but I expect it is easier to do the latter (and TBH not needing to think about this too much would be a plus).
Upon looking into it though, this doesn't seem supported at present according to this issue, and running blacken-docs on some code which prints a double-quoted __repr__ in a >>> doctest block does not affect said double quoting.
My interpretation of that issue and the other one it links to is that blacken-docs does not support code blocks in the format:
"""
Docstring starts here.
Code example comes next:
>>> from some_module import some_func
>>> some_func(1, 2)
"1+2"
"""
I've tried rewriting code in the suggested format:
"""
Docstring starts here.
Code example comes next:
.. code-block:: python
>>> from some_module import some_func
>>> some_func(1, 2)
"1+2"
"""
but this gives me
code block parse error Cannot parse: 1:0: >>> from some_module import some_func
So I'm not going to use blacken-docs or the more verbose .. code-block: python format, perhaps in time it'll be supported.

Triple quotation in python

So I understand that if I do the following
print """ Anything I
type in here
works. Multiple LINES woohoo!"""
But what if following is my python script
""" This is my python Script. Just this much """
What does the above thing do? Is it taken as comment? Why is it not a syntax error?
Similarly, if I do
"This is my Python Script. Just this. Even with single quotes."
How are the above two scripts interpreted?
Thanks
The triple quotes ''' or """ are just different ways of representing strings. The advantage of triple quotes is that it can span multiple lines and sometimes serve as docstrings.
The reason:
"hadfasdfas"
doesn't raise any error is because python simply creates the string and then doesn't assign it to anything. For the python interpreter, it is perfectly fine if you have a pointless statement in your code as long as there are no syntax or semantics errors
Hope that helps.
The string is just evaluated, and the interpreter noticing it wasn't assigned to anything, throws it away.
But in some special places, this string is actually assigned to the __doc__ property of the item:
def func(arg):
"""
Does stuff. This string will be evaluated and assigned to func.__doc__.
"""
pass
class Test:
"""
Same for Test.__doc__
"""
pass
At the top of module.py:
"""
module does stuff. this will be assigned to module.__doc__
"""
def func():
...
In addition to #sshashank124 answer I have to add that triple quoted strings are also used in testing https://docs.python.org/2/library/doctest.html
So consider this code snippet:
def some_function(x, y):
"""This function should simply return sum of arguments.
It should throw an error if you pass string as argument
>>> some_function(5, 4)
9
>>> some_function(-5, 4)
-1
>>> some_function("abc", 4)
Traceback (most recent call last):
...
ValueError: arguments must numbers
"""
if type(x, str) or type(y, str):
raise ValueError("arguments must numbers")
else:
return x + y
if __name__ == "__main__":
import doctest
doctest.testmod()
If you import this tiny module, you'll get the some_function function.
But if you invoke this script directly from shell, tests given in the triple quoted string will be evaluated and the report will be printed to the output.
So triple quoted strings can be treated as values of type string, as comment, as docstrings and as containers for unittests.

Text wrapping from optparse help string on multiple lines

I am trying to make my help strings helpful. To do this I have a function Function() with a doc string something like
def Function(x):
""" First line describing what Function does
Keyword Arguments
x = float -- A description of what x does that may be long
"""
Having done this I think have something like this at the end
def parse_command_line(argvs):
parser = optparse.OptionParser()
parser.add_option("-f","--Function", help=Function.__doc__,metavar="Bs" )
(options,arguments) = parser.parse_args(argvs)
return options, arguments
options, arguments = parse_command_line(sys.argv)
The trouble occurs when calling the program with -h or --help The output is line wrapped by OptParse, this means the KeyWord arguments are not started on a new line, is it possible to stop OptParse from wrapping the output or is there a better way to do this?
In case of interest, I have written two such formatter classes for argparse. The first supports much of the mediaWiki, MarkDown, and POD syntaxes (even intermixed):
import MarkupHelpFormatter
MarkupHelpFormatter.InputOptions["mediawiki"] = True
parser = argparse.ArgumentParser(
description="""...your text, with mediawiki markup...""",
epilog='...',
formatter_class=MarkupHelpFormatter.MarkupHelpFormatter
)
The other is called "ParagraphHelpFormatter". It merely wraps text like the default argparse formatter, except that it respects blank lines.
Both are at http://derose.net/steve/utilities/PY/MarkupHelpFormatter.py, and
licensed as CCLI Attribution-Share-alike. They format for ANSI terminal
interfaces. Not highly polished (for example, auto-numbering is unfinished), but you may find them helpful.
argparse provides you with a formatter for raw formatting, ie your lines wont get wrapped
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
optparse also lets you set the formatter.. I suppose you can write your own formatter, but theres nothing provided that i know of

Scripts that Rely on Docstrings?

From the 2.7.2 docs, Section 6, Modules:
Passing two -O flags to the Python interpreter (-OO) will cause the bytecode compiler to perform optimizations that could in some rare cases result in malfunctioning programs. Currently only __doc__ strings are removed from the bytecode, resulting in more compact .pyo files.
This got my attention:
Since some programs may rely on having these available, you should only use this option if you know what you’re doing.
Are there any cases where removing a script's docstrings might logically break some dependency or other aspect of a code's functionality, disregarding any syntactic errors?
EDIT
Why would removing comments break a help statement? It doesnt seem to do so in the interpreter.
>>> help('import_pi')
Help on module import_pi:
NAME
import_pi
FILE
/home/droogans/py/import_pi.py
FUNCTIONS
print_pi()
DATA
pi = 3.1415926535897931
>>> import import_pi()
>>> import_pi.__doc__
>>>
>>> print import_pi.print_pi.__doc__
Convert a string or number to a floating point number, if possible.
For example ply is a module that does lexing and parsing which uses docstrings to describe the grammar. Stripping docstrings would break the code.
The -OO option only affects whether a doc string is stored -- it does not affect parsing. For example the following code works with and without optimizations enabled:
def f():
'Empty docstring'
assert f() is None
The programs that will break with the docstring optimization enabled are ones that rely on the contents of the docstring. Paul Hankin mentioned Ply which is tool that uses docstrings for its dispatch logic. Another example is the doctest module which uses the contents of docstrings for tests.
Here's a simple example of code that won't work with the -OO optimization enabled:
def f():
'30 + 40'
return eval(f.__doc__)
print f()
Note help() will still work with the -OO optimization enabled, but it will only find the function name, arguments, and module, but not the docstring:
>>> help(f)
Help on function f in module __main__:
f()

Categories