================================== Documenting the public interface ================================== This library provides two decorators that document the public visibility of the names in your module. They keep your module's ``__all__`` in sync so you don't have to. Also included is a function that you can put at the bottom of your module to simply infer all the public names, and populate the ``__all__`` for you. Background ========== ``__all__`` is great. It has both functional and documentation purposes. The functional purpose is that it `directly controls`_ which module names are imported by the ``from import *`` statement. In the absence of an ``__all__``, when this statement is executed, every name in ```` that does not start with an underscore will be imported. This often leads to importing too many names into the module. That's a good enough reason not to use ``from import *`` with modules that don't have an ``__all__``. In the presence of an ``__all__``, only the names specified in this list are imported by the ``from import *`` statement. This in essence gives the ```` author a way to explicitly state which names are for public consumption. And that's the second purpose of ``__all__``; it serves as module documentation, explicitly naming the public objects it wants to export. You can print a module's ``__all__`` and get an explicit declaration of its public API. The problem with __all__ ======================== ``__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! @public ======= This package provides a way to declare a name's public visibility right at the point of its declaration, and to infer the name to export from that definition. In this way, a module's author never explicitly sets the ``__all__`` so there's no way for it to get out of sync. This package, and Python `issue 26632`_, propose just such a solution, in the form of a ``public()`` function that can be used as either a decorator, or a callable. .. code-block:: pycon >>> from public import public You'll usually use this as a decorator, for example: .. code-block:: pycon >>> @public ... def foo(): ... pass or: .. code-block:: pycon >>> @public ... class Bar: ... pass The ``__all__`` after both of those code snippets has both names in it: .. code-block:: pycon >>> print(__all__) ['foo', 'Bar'] .. note:: You do not need to initialize ``__all__`` in the module, since ``public()`` will do it for you. Of course, if your module *already* has an ``__all__``, it will append any new names to the existing list. Function call form ================== The requirements to use the ``@public`` decorator are simple: the decorated thing must have a ``__name__`` attribute. Since you'll overwhelmingly use it to decorate functions and classes, this will almost always be the case. If the object has a ``__module__`` attribute, that string is used to look up the module object in ``sys.modules``, otherwise the module is extracted from the globals where the decorator is called. There's one other common use case that isn't covered by the ``@public`` decorator. Sometimes you want to declare simple constants or instances as publicly available. You can't use the ``@public`` decorator for two reasons: constants don't have a ``__name__`` and Python's syntax doesn't allow you to decorate such constructs. To solve this use case, ``public()`` is also a callable function accepting keyword arguments. An example makes this obvious. .. invisible-code-block: pycon >>> reset() .. code-block:: pycon >>> public(SEVEN=7) 7 >>> public(a_bar=Bar()) <...Bar object ...> The module's ``__all__`` now contains both of the keys: .. code-block:: pycon >>> print(__all__) ['SEVEN', 'a_bar'] and as should be obvious, the module contains name bindings for these constants: .. code-block:: pycon >>> print(SEVEN) 7 >>> print(a_bar) <....Bar object at ...> Multiple keyword arguments are allowed: .. code-block:: pycon >>> public(ONE=1, TWO=2) (1, 2) >>> print(__all__) ['SEVEN', 'a_bar', 'ONE', 'TWO'] >>> print(ONE) 1 >>> print(TWO) 2 You'll notice that the functional form of ``public()`` returns the values in its keyword arguments in order. This is to help with a use case where some linters complain because they can't see that ``public()`` binds the names in the global namespace. In the above example they might report erroneously that ``ONE`` and ``TWO`` aren't defined. To work around this, when ``public()`` is used in its functional form, it will return the values in the order they are seen [#]_ and you can simply assign them to explicit local variable names. .. code-block:: pycon >>> a, b, c = public(a=3, b=2, c=1) >>> print(__all__) ['SEVEN', 'a_bar', 'ONE', 'TWO', 'a', 'b', 'c'] >>> print(a, b, c) 3 2 1 It also works if you bind only a single value. .. code-block:: pycon >>> d = public(d=9) >>> print(__all__) ['SEVEN', 'a_bar', 'ONE', 'TWO', 'a', 'b', 'c', 'd'] >>> print(d) 9 @private ======== You might also want to be explicit about your private, i.e. non-public, names. This library provides an ``@private`` decorator for this purpose. While it mostly serves for documentation purposes, this decorator also ensures that the decorated object's name does *not* appear in the ``__all__``. .. invisible-code-block: pycon >>> reset() .. code-block:: pycon >>> from public import private >>> @public ... def foo(): pass >>> print(__all__) ['foo'] >>> @private ... def foo(): pass >>> print(__all__) [] You can see here that ``foo`` has been removed from the ``__all__``. It's okay if the name doesn't appear in ``__all__`` at all: .. invisible-code-block: pycon >>> reset() .. code-block:: pycon >>> @private ... class Baz: ... pass >>> print(__all__) [] In this case, ``Baz`` never appears in ``__all__``. Like with ``@public``, the ``@private`` decorator will initialize ``__all__`` if needed, but if it exists in the module, it must be a list. There is no functional API for ``@private``. Inferring __all__ ================= If you don't like using the decorators, you can instead infer and populate the contents of ``__all__`` by calling the ``populate_all()`` function at the bottom of your module. This uses heuristics to pick out some names from the module, adding them to ``__all__`` if they meet the following criteria: * The name does not start with an underscore. * The name is not bound to a module object. This prevents imported modules from being added. * The object the name is bound to does not appear to be defined in some other module. This prevents most from-imports from being added, but note that this can be fooled if you import simple types (such as an ``int`` or a ``str``) from another module (e.g. ``from sys import abiflags``), because simple types don't have a ``__module__`` attribute. For example, if your Python module looks like this: .. literalinclude:: popall_example.py :language: python :linenos: when you import this module, the ``__all__`` will be populated with the names matching the above heuristics. .. invisible-code-block: pycon >>> example = import_example('popall_example.py') .. code-block:: pycon >>> example.__all__ ['foo', 'Foo', 'fooint'] In this case, you can see that the module has an ``__all__`` set to ``['foo', 'Foo', 'fooint']`` but note that neither ``_foobool`` nor ``populate_all`` are added. If the inferencing misses some names you want to publicly export, you can always add them explicitly by using ``@public`` or appending to ``__all__``. .. note:: This function only adds new names to ``__all__``. Caveats ======= There are some important usage restrictions you should be aware of: * Only use ``@public`` and ``@private`` on top-level object. Specifically, don't try to use either decorator on a class method name. While the declaration won't fail, you will get an exception when you attempt to ``from import *`` because the name pulled from ``__all__`` won't be in the module's globals. * If you explicitly set ``__all__`` in your module, be sure to set it to a list. Some style guides require ``__all__`` to be a tuple, but since that's immutable, as soon as ``@public`` tries to append to it, you will get an exception. Best practice is to not set ``__all__`` explicitly; let ``@public`` and ``@private`` do it! * If you still want ``__all__`` to be immutable, put the following at the bottom of your module: .. code-block:: python __all__ = tuple(__all__) .. [#] This is ordering is guaranteed by `PEP 468 `_. .. _`issue 26632`: http://bugs.python.org/issue26632 .. _builtins: https://docs.python.org/3/library/builtins.html .. _`directly controls`: https://docs.python.org/3/tutorial/modules.html#importing-from-a-package .. _`actively prohibited`: http://pep8.readthedocs.io/en/latest/intro.html?highlight=e402#error-codes .. _`out of sync`: http://bugs.python.org/issue23883 .. _implementations: http://bugs.python.org/issue22247#msg225637 .. _Sphinx: http://www.sphinx-doc.org/en/stable/