4

According to PEP 257 the docstring of command line script should be its usage message:

The docstring of a script (a stand-alone program) should be usable as its "usage" message, printed when the script is invoked with incorrect or missing arguments (or perhaps with a "-h" option, for "help"). Such a docstring should document the script's function and command line syntax, environment variables, and files. Usage messages can be fairly elaborate (several screens full) and should be sufficient for a new user to use the command properly, as well as a complete quick reference to all options and arguments for the sophisticated user.

And the the docstring shall be the first string as module level, before anything else, to be available as __doc__.

Now, I'm also using docopt as a usage message parser, so I just have to write the doc string, and it's building the command line parser on its own, and that's great.

_("""...""")

What's not that great, is that I can't find a way to mark the docstring as i18nable for gettext, so I can convert it to other languages when given to docopt. At the time being the only solution I got is to make the usage and help messages stay in english, when all the application's other strings are translated!

As the PEP 20 states:

There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.

What would be the best way to get around the limitation of not being able to mark the docstring as translatable elegantly?

N.B.: here we consider that I'm doing the gettext.install() in the __init__.py module so that _() is present in the builtins before even __doc__ is being parsed.

Community
  • 1
  • 1
zmo
  • 24,463
  • 4
  • 54
  • 90
  • 2
    I suspect that internationalization of the usage message would have to be supported by `docopt`. Have you had a look at the source code? Maybe you can make the change yourself and submit a pull request to be incorporated upstream. – Pedro Romano May 16 '14 at 14:35
  • well, I did not look *yet* at that, because I never thought I'd be the first to get around such a problem :-) If there is indeed no way, I'll send a patch! – zmo May 16 '14 at 14:36
  • after looking through and chatting with a few friends, actually, it's not possible to patch python to support marking docstrings. Because docstrings are evaluated at compile time, whereas `_()` is a function, thus being evaluated at run time, that would mean moving docstrings to runtime evaluation that could have a cascade of consequences. – zmo May 21 '14 at 13:35

3 Answers3

1

At the time being, here's a solution I'm thinking of being like:

"""\
This is the docstring
"""

import docopt
if __name__ == "__main__":
    try:
        args = docopt.docopt(_("{docstring}").format(docstring=__doc__))
    except KeyError:
        args = docopt.docopt(_("{docstring}")) # string which will be replaced by the i18ned one.

I don't find that elegant, because even though exceptions are ok in python, I think that they should be kept for something exceptional not something in the use case of the application.

It's also being a pretty naughty hack that will get the docstring format in the gettext instead of the __docopt__ text which won't help translators as they will have to get back to source code...

zmo
  • 24,463
  • 4
  • 54
  • 90
1

I finally found the only good solution to parse docstrings:

-D
--docstrings
    Extract module, class, method, and function docstrings.  These do
    not need to be wrapped in _() markers, and in fact cannot be for
    Python to consider them docstrings. (See also the -X option).

will extract all docstrings. So the only one that needs to be translated can be translated using:

args = docopt.docopt(_(__doc__))
zmo
  • 24,463
  • 4
  • 54
  • 90
  • What command takes this parameter, and where is the documentation for that command? – clacke Jul 15 '17 at 08:34
  • 1
    I got to say that was a long while ago, so I had to look up again to find out what I was talking about ^^ so it's for the [pygettext](https://manpages.debian.org/unstable/python3.6/pygettext3.6.1.en.html) tool! – zmo Jul 15 '17 at 13:07
0

Another way:

if __name__ == "__main__":
    if hasattr(vars()['__builtins__'], '_') and not 'NullTranslations' in str(_):
        args = docopt.docopt(_("USAGE MESSAGE"))
    else:
        args = docopt.docopt(__doc__)

which is not using an exception, but tests for typing using the string representation of a method, and looksup that method in the builtins module... Which is not really better than the other option.

And that one is also a pretty bad and unelegant hack, because translators will have to refer to the source code to look up the doc string. Or I'll have to have two times the contents of the docstring in the code.

zmo
  • 24,463
  • 4
  • 54
  • 90