1

TL;DR Is a docstring supposed to be writable as simply as

x.__doc__ = 'string' ?

It seems to work in 3.5.2.

I've written function for taking and checking GUI keyboard inputs. It's here on Code Review if anybody needs the details.

A fundamental part of it is the ability for it to use supplied checking functions, to check whether the input strings are valid in any given context. For better or worse, I decided to use the check function docstring to supply help information to the GUI, which would then parse it, as it was easy to do.

This is an example, the 'pair of floats' check function, the encoding of the help hints into the docstring is fairly obvious.

def float_pair(in_str):
    """[f,f] Two floats separated by a comma
    [end_help]
    in_str     input string which should represent float, float
    raise      ValueError if that's not true
    return     a list of the two values
    """
    fields = in_str.split(',')
    if len(fields) != 2:
        raise ValueError('need two floats separated by one comma')
    output = [float(f) for f in fields]  
    return output

I now want to add a capability to check whether a float is in a certain range, ideally passing that range to a check function factory. Ideally the docstring of the returned function will contain the range, so that the help the GUI gives is relevant. I therefore need to set the docstring at run time.

A lot of searching has left me confused about whether a docstring is supposed to be writable or not. I've seen this question. Unfortunately, I'm only a physicist turned programmer, not a skilled metaprogrammer, so I don't understand the finer points.

So I tried it in the simplest possible way, and it seems to work, at least on python 3.5.2. I'm going to up-version eventually, if for no more than f strings. My concern is whether it's intended to keep working in future releases, or whether it will be 'fixed' to readonly.

def float_range_factory(x, y):
    def float_range(z):
        fz = float(z)
        if x<=fz<y:
            return fz
        raise ValueError('input {} must be in the interval [{}, {}]'.format(z, x, y))
    float_range.__doc__ = '[f range] Return float in the interval [{}, {}]'.format(x, y)
    return float_range
Neil_UK
  • 1,043
  • 12
  • 25

1 Answers1

2

The functools module in the standard library provides an example of assigning to __doc__ directly:

class cached_property:
    def __init__(self, func):
        self.func = func
        self.attrname = None
        self.__doc__ = func.__doc__
        self.lock = RLock()

    [...]

Note that the __doc__ attribute is apparently only set from a docstring at compile-time; I tried something like

def foo(x):
    def bar():
        f"""Return {x}"""
        return x
    return bar

but foo(3).__doc__ is None, as if there was no docstring present in the definition of bar.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • I think your conclusion is not correct. From the "Lexical analysis" chapter: _"Formatted string literals cannot be used as docstrings, even if they do not include expressions."_. – VPfB Apr 08 '20 at 16:28
  • That makes sense; it's probably easier to ignore f-strings altogether than to try to figure out if it can evaluated or not at the point it would be needed. – chepner Apr 08 '20 at 16:54
  • (Looking back, I think I meant "parse time" when I said "compile time", but a direct quote that says f-strings cannot be used for docstrings is better than speculating.) – chepner Apr 08 '20 at 16:55