@@ -14,10 +14,10 @@ Abstract
1414
1515This PEP proposes adding two new builtin functions, ``public() `` and ``private() ``, which document
1616the public interface of a module by keeping its ``__all__ `` synchronized with the names actually
17- defined in that module. Both are used as decorators (``@public `` and ``@private ``) on class and
18- function definitions, so that a name's visibility is declared exactly once, at the point where the
19- name is defined. ``public() `` additionally has a function call form for names that cannot be
20- decorated, such as constants.
17+ defined to be public in that module. Both are used as decorators (``@public `` and ``@private ``) on
18+ class and function definitions, so that a name's visibility is declared exactly once, at the point
19+ where the name is defined. ``public() `` additionally has a function call form for names that cannot
20+ be decorated, such as constants.
2121
2222For example:
2323
@@ -49,11 +49,11 @@ This PEP is an adjunct to :pep:`842` and PEP 843; see `Relationship to PEP 842 a
4949Motivation
5050==========
5151
52- The module global variable :attr: `~module.__all__ ` is the mechanism Python currently
53- defines for declaring a module's public names. However, ``__all__ `` suffers from a well known
54- problem: it is typically defined as a separate list often far from the objects whose names are
55- contained in it. An object defined at one point in the file is repeated as a string literal in an
56- `` __all__ `` list somewhere else, usually at the top of the file.
52+ The module global variable :attr: `~module.__all__ ` is the mechanism Python currently defines for
53+ declaring a module's public names. However, ``__all__ `` suffers from a well- known problem: it is
54+ typically defined as a separate list often far from the objects whose names are contained in it. An
55+ object defined at one point in the file is repeated as a string literal in an `` __all__ `` list
56+ somewhere else, usually at the top of the file.
5757
5858Nothing keeps the two in sync, leading to these problems:
5959
@@ -83,9 +83,9 @@ enough, and useful enough, to be spelled without a third-party dependency. Thus
8383``__all__ `` already defines the public API
8484------------------------------------------
8585
86- It is sometimes said that Python has no way to express which names in a module are public and which
87- are private, and that ``__all__ `` is merely a convention governing ``from spam import * ``. However,
88- the :ref: `language reference <import >` explicitly says:
86+ It is sometimes said that Python has no way to express which names in a module are public and
87+ which are private, and that ``__all__ `` is merely a convention governing ``from spam import * ``.
88+ However, the :ref: `language reference <import >` explicitly says:
8989
9090 The *public names * defined by a module are determined by checking
9191 the module's namespace for a variable named ``__all__ ``; if defined,
@@ -133,6 +133,14 @@ Specification
133133
134134Two new builtins are added: ``public() `` and ``private() ``.
135135
136+ This PEP concerns module-level visibility only. ``public() `` and ``private() `` declare which of a
137+ module's global names make up its public interface, and they do so by maintaining ``__all__ ``, which
138+ is defined for modules and nothing else. Visibility in any other scope is explicitly excluded:
139+ class attributes and methods, names local to a function, and names bound in nested scopes are all
140+ untouched by this proposal. Python has no ``__all__ `` equivalent for those scopes, and this PEP
141+ does not propose one. Whether a method is part of a class's public interface remains, as today, a
142+ matter of naming convention and documentation.
143+
136144``public() ``
137145------------
138146
@@ -217,17 +225,23 @@ author's intent; ``@public`` is what makes that intent observable.
217225.. note ::
218226
219227 ``private() `` does *not * support a function call form, as no valid use case for it has been
220- identified or requested by users of the ``atpublic `` module . See `Open Issues `_ for
221- further discussion.
228+ identified or requested by users of the ``atpublic `` package . See `Open Issues `_ for further
229+ discussion.
222230
223231
224232Restrictions
225233------------
226234
227- Note that because ``@public `` is primarily used to keep the ``__all__ `` module global in sync, only
228- module-level objects may be decorated . Decorating a method inside a class body is not supported,
235+ Because ``@public `` and `` @private `` exist to keep the ``__all__ `` module global in sync, only
236+ module-level objects may be declared . Decorating a method inside a class body is not supported,
229237since ``__all__ `` documents module contents, not class contents.
230238
239+ Neither function inspects the scope it is called from, so this misuse is not currently diagnosed.
240+ A decorator applied to a method appends the method's name to the enclosing *module's * ``__all__ ``,
241+ and a function call form used in a class body binds its keywords in the module globals rather than
242+ in the class body. Neither outcome is likely to be what the author intended. Whether these cases
243+ should raise an exception instead is an `Open Issues `_ question.
244+
231245Because ``__all__ `` must be mutable for these functions to append to it, a module that assigns
232246``__all__ `` itself must assign a list. A module that wants an immutable ``__all__ `` can freeze it
233247after the last declaration with ``__all__ = tuple(__all__) ``.
@@ -288,9 +302,9 @@ explicitly. Given:
288302 ``SEVEN `` is bound in the module's globals by a function that reaches into its caller's frame.
289303Nothing about that binding is visible in the syntax tree. A type checker, linter, or language
290304server reading the source sees a bare function call and no assignment, and will therefore report
291- ``SEVEN `` as undefined at every use site. A soft keyword like ``export SEVEN = 7 `` (such as
292- proposed by :pep: `842 `) has no such problem, because syntax is by construction visible to anything
293- that parses the file. This, and not the DRY objection raised in :pep: `842 `, is the real cost of
305+ ``SEVEN `` as undefined at every use site. A soft keyword like ``export SEVEN = 7 ``, as proposed by
306+ :pep: `842 `, has no such problem, because syntax is by construction visible to anything that parses
307+ the file. This, and not the DRY objection raised in :pep: `842 `, is the real cost of
294308choosing a builtin over a keyword.
295309
296310This could easily be alleviated by future modifications to linting tools, so that they explicitly
@@ -322,8 +336,8 @@ Here the binding is a plain assignment, visible to every tool that parses Python
322336transition aid rather than the recommended spelling, and it should not be needed for long.
323337
324338The conclusion is that the data and type alias use cases, which are the places a decorator
325- genuinely cannot be utilized, do not require new syntax at all. They require a function call that
326- tools can learn to read, alleviating the need for a dedicated, new ``export `` keyword.
339+ genuinely cannot be utilized, do not require new syntax at all. A function call that tools can
340+ recognize serves just as well, without the need for a new, dedicated ``export `` keyword.
327341
328342
329343.. _pep-844-urgency :
@@ -369,7 +383,7 @@ pressing, but because the cheaper alternative stops being available once the exp
369383
370384.. _pep-844-performance :
371385
372- Import time performance
386+ Import- time performance
373387-----------------------
374388
375389When this idea was informally floated with core developers some years ago, before either :pep: `842 `
@@ -522,7 +536,7 @@ module" that pulls names out of private submodules must currently write each nam
522536
523537 ``Widget `` is named once to import it, and twice more to export it. That's a big violation of DRY!
524538Hand-maintaining ``__all__ `` would name it only twice, so for re-exports specifically, ``public() ``
525- is not merely unhelpful, it is a step backwards.
539+ is not merely unhelpful, but a step backwards.
526540
527541The decorator form of ``@public `` is unavailable here because there is nothing to decorate, and the
528542function call form of ``public() `` requires naming the binding explicitly. This is exactly the gap
@@ -663,18 +677,18 @@ Leaving ``atpublic`` on PyPI is the status quo option. Users who want to opt in
663677functionality can simply add that library as a dependency and import the functions (or use the
664678``pip install atpublic[install] `` extra to populate builtins).
665679
666- However, if this *is * a problem worth solving now, then leaving this in a third-party module on PyPI
667- doesn't serve our users adequately. The need to include a dependency and an explicit import may
668- be just enough of a hurdle (albeit small) to stop widespread use of it. Adding it to builtins gives
669- this pattern a promotional endorsement that will gain in popularity .
680+ However, if this *is * a problem worth solving now, then leaving this in a third-party package on
681+ PyPI doesn't serve our users adequately. The need to include a dependency and an explicit import
682+ may be just enough of a hurdle (albeit small) to stop widespread use of it. Adding it to builtins
683+ endorses the pattern in a way that should broaden its adoption .
670684
671685
672686A new standard library module instead of builtins
673687-------------------------------------------------
674688
675689This would eliminate the third-party dependency problem, but still leaves the explicit import
676690usability cost. In addition, there's no obvious place to add it to the stdlib *other than * in
677- builtins. Two functions likely isn 't worth the cost of a new top-level module. Besides, since
691+ builtins. Two functions likely aren 't worth the cost of a new top-level module. Besides, since
678692``__all__ `` is in a sense built into Python, these functions should be built in too.
679693
680694
@@ -690,13 +704,17 @@ Open Issues
690704 visibility ergonomics.
691705* Should ``private() `` support a function call form, for symmetry? ``atpublic `` does not provide
692706 one and no need for it has ever been demonstrated or requested.
707+ * Should ``public() `` and ``private() `` diagnose being called outside module scope? Neither
708+ inspects its calling scope today, so ``@public `` on a method silently adds the method's name to
709+ the module's ``__all__ ``. Raising an exception would be friendlier, at the cost of a scope check
710+ on every call, which bears on :ref: `pep-844-performance `.
693711* Should the standard library itself adopt these decorators, and if so on what schedule? This
694712 question is entangled with :ref: `pep-844-performance ` and should be settled with startup
695713 measurements in hand. Also, as with all new capabilities (such as lazy imports), Python's policy
696714 is generally not to wholesale update the stdlib to embrace the new functionality. These new
697715 functions can be utilized opportunistically in modules where the most benefit can be gained, or
698716 when a module undergoes substantial rewrite.
699- * Import time benchmarks for a C implementation are outstanding.
717+ * Import- time benchmarks for a C implementation are outstanding.
700718
701719
702720Acknowledgements
0 commit comments