pydoctor icon indicating copy to clipboard operation
pydoctor copied to clipboard

Document it if API might not be available

Open not-my-profile opened this issue 3 years ago • 3 comments

For the following module:

try:
    from foobar import FooBar
except ModuleNotFoundError:
    class FooBar:
        """Stub for Foobar"""

if platform.system() == 'Linux':
    def greet_os():
        print('Hello Tux!')

Pydoctor generates the documentation:

image

Note that there is absolutely no indication that FooBar and greet_os might not be available. I think that there should be some, e.g:

  • only available if a ModuleNotFoundError exception occurs
  • only available if platform.system() == 'Linux'

I get that there can be arbitrarily many such conditions but I think at least the outmost one should be documented, so that readers don't get the wrong impression that the API member is always available.

not-my-profile avatar Feb 03 '22 08:02 not-my-profile

I am not -1 on this

just my feedback.

you can exclude code based on a variety of conditions... so I am not sure if platform.system() is enough.

I think that the fix here is to update the docstrings of "greet_os" and explicit document that it is only available on Linux.


to generalized, you might have an API that might only be available for private access vs public access... or all kind of crazy API designs :)

adiroiban avatar Feb 03 '22 10:02 adiroiban

platform.system() was just an example. I propose that any if conditions should be automatically documented (doesn't have to be shown in verbatim, maybe only on hover if it's complex).

I think the advantage of the automatic documentation is that:

  1. it is always available even for undocumented code
  2. it lets readers quickly recognize such shenanigans, without having to read the docstrings, e.g. rustdoc shows a colorful blob: image

not-my-profile avatar Feb 03 '22 10:02 not-my-profile

What would you do for stuff like this?

if sys.version_info[:2] >= (3, 8):
    # Since Python 3.8 "foo" is parsed as ast.Constant.
    def get_str_value(expr:ast.expr) -> Optional[str]:
        if isinstance(expr, ast.Constant) and isinstance(expr.value, str):
            return expr.value
        return None
    def get_num_value(expr:ast.expr) -> Optional[Number]:
        if isinstance(expr, ast.Constant) and isinstance(expr.value, Number):
            return expr.value
        return None
    def _is_str_constant(expr: ast.expr, s: str) -> bool:
        return isinstance(expr, ast.Constant) and expr.value == s
else:
    # Before Python 3.8 "foo" was parsed as ast.Str.
    def get_str_value(expr:ast.expr) -> Optional[str]:
        if isinstance(expr, ast.Str):
            return expr.s
        return None
    def get_num_value(expr:ast.expr) -> Optional[Number]:
        if isinstance(expr, ast.Num):
            return expr.n
        return None
    def _is_str_constant(expr: ast.expr, s: str) -> bool:
        return isinstance(expr, ast.Str) and expr.s == s

We are currently ignoring what’s in the else block .

tristanlatr avatar May 23 '22 00:05 tristanlatr