[docs] [issue13386] Document documentation conventions for optional args

Terry J. Reedy report at bugs.python.org
Fri Nov 18 05:29:32 CET 2011

Terry J. Reedy <tjreedy at udel.edu> added the comment:

>From Ezio's original post: '''
If a function has optional arguments but it doesn't accept keyword arguments, the "func([arg1])" notation is used instead. ... The notation "func([arg=default])" should never be used, and "func([arg])" should be used only when keyword args are not accepted.

In the following, I give objections to this PO (position only) rule and suggest an alternative ND (no default) rule: use 'name=default' when there is a default and '[name]' when there is not'.

The issue of whether an argument is required or optional is orthogonal to whether it can be passed by both position and name, only by name, or only by position. All combinations are possible. Optional arguments may or may not have a definition-time (or even run-time) default value, regardless of how passed. (In Python, use of *args and **kwds allows args to be optional without default.)

In the CPython stdlib, I think position-only arguments only occur with some, but only some, C functions. One can emulate such C functions in Python by doing the equivalent of what is going on with such C functions. Use a collective *varargs in the definition while naming the required and optional components of varargs in the doc as if they were the actual parameters. But I think we agree that emulating a limitation of C in Python is a bad idea.

So by using [] to mean both 'argument is optional' and 'function only take parameters by position' (or at least 'this parameter can only be passed to this function by position'), we are simultaneously documenting an intended and permanent feature of the Python function and a possibly temporary and unwanted side-effect of the current CPython implementation of that function. I think a separate PO indication might be better.

1: The PO rule goes against the effort to separate the Python language from the CPython implementation. With it, the doc for a function does not apply to other implementations that do not have the PO limitation for that function.

2: The PO rule is incomplete. It only marks an arg as position-only if it is optional, but not if it is required. And even if marking one arg as PO means that other args of the function might be, so 'watch out', there is still no special marking for a function with only required PO args. 

A separate sentence like "For CPython, all args must be passed by position." would solve both of the above problems.

3: The PO rule does not account for the possibility that an argument can be passed by keyword, perhaps only by keyword, but have no default. This is possibly in Python with **kwds in the def and recognized optional names in the doc. With 'name=default' and '[name]' not allowed, how should such an argument be documented is a signature?

4: The PO rule omits useful information on defaults from the place of prominence - the signature header for the entry. Sometimes the information, needed by some users and all implementers, gets omitted altogether. For example, the doc string and manual entry give the signature for str.startwith as
  str.startswith(prefix[, start[, end]])
The unmentioned defaults are None, None.

In summary, the PO rule primarily indicates, but only for optional args, whether the arg can be passed by keyword or not. It secondarily indicates, but only if it can be passed by keyword, what its default is. But if fails if the arg can be passed by keyword but does not have a default. It also fails, in its primary role, for required args.

To me, this is all mixed up. Method of passing is not related to optionality. What is special about optional args, regardless of how passed, is the default value, if it has one. The ND rule is to give exactly this information. With an implementation-independent signature and a separate note on passing method, when needed, it solves all the problems listed above. For .startswith, I would like to see something like
   str.startswith(prefix, start=None, end=None) ...
   CPython: pass args by position only.

#13355 illustrates Eric's point with a twist.
"random.triangular(low, high, mode) 
Return a random floating point number N such that low <= N <= high and with the specified mode between those bounds. The low and high bounds default to zero and one. The mode argument defaults to the midpoint between the bounds, giving a symmetric distribution."

The *actual* default for mode is None. The function *usually* acts if the default were as described. Twist 1 is that it does not actually calculate the midpoint, as it is not actually needed. Twist 2 is that there is currently a bug (easily fixed) such that triangular does not work if low>=high and mode is not specified, whereas it does work if the true default None is passed ;-). So one needs to know the real default to avoid the bug.

Of course, as I said on the issue, all defaults should be given in the signature (by either PO or ND rule):
"random.triangular(low=0.0, high=1.0, mode=None) ..."

And yes, +1 to documenting visible document conventions both in the documenting howto *and* in the docs themselves.


Python tracker <report at bugs.python.org>

More information about the docs mailing list