
On Jul 23, 2019, at 09:20, Ethan Furman <ethan@stoneleaf.us> wrote:
On 07/23/2019 08:44 AM, Steve Dower wrote:
The @public decorator is basically: def public(fn): __all__.append(fn.__name__) return fn It's trivial, but it adds a runtime overhead that is also trivially avoided by putting the name in __all__ manually. And once it's public API, we shouldn't be making it too easy to rename the function anyway ;)
The run-time overhead added by executing @public is trivially trivial. ;) But your argument about the too-easy change of a public API strikes home.
I think a safer @public would be one that verifies the function is in `__all__` and raises if it is not.
That actually defeats the purpose of @public IMHO. There should be exactly one source of truth, and that ought to be the @public decorator. From https://public.readthedocs.io/en/latest/ “”" __all__ has two problems. First, it separates the declaration of a name’s public export semantics from the implementation of that name. Usually the __all__ is put at the top of the module, although this isn’t required, and in some cases it’s actively prohibited. So when you’re looking at the definition of a function or class in a module, you have to search for the __all__ definition to know whether the function or class is intended for public consumption. This leads to the second problem, which is that it’s too easy for the __all__ to get out of sync with the module’s contents. Often a function or class is renamed, removed, or added without the __all__ being updated. Then it’s difficult to know what the module author’s intent was, and it can lead to an exception when a string appearing in __all__ doesn’t match an existing name in the module. Some tools like Sphinx will complain when names appear in __all__ don’t appear in the module. All of this points to the root problem; it should be easy to keep __all__ in sync! “”” Think of it this way: __all__ is an implementation detail, and @public is the API for extending it. Cheers, -Barry