PEP 844 – public and private builtins
- Author:
- Barry Warsaw <barry at python.org>
- Discussions-To:
- Discourse thread
- Status:
- Draft
- Type:
- Standards Track
- Created:
- 05-Aug-2026
- Python-Version:
- 3.16
- Post-History:
- 11-Aug-2026
Abstract
This PEP proposes adding two new builtin functions, public() and private(), which document
the public interface of a module by keeping its __all__ synchronized with the names actually
defined to be public in that module. Both are used as decorators (@public and @private) on
class and function definitions, so that a name’s visibility is declared exactly once, at the point
where the name is defined. public() additionally has a function call form for names that cannot
be decorated, such as constants.
For example:
# spam.py
@public
class Public:
...
@private
class Private:
...
public(SEVEN=7)
>>> import spam
>>> spam.__all__
['Public', 'SEVEN']
The proposed semantics are those of the third-party atpublic package, which has provided this functionality since 2016.
This PEP is an adjunct to PEP 842 and PEP 843; see Relationship to PEP 842 and PEP 843.
Motivation
The module global variable __all__ is the mechanism Python currently defines for
declaring a module’s public names. However, __all__ suffers from a well-known problem: it is
typically defined as a separate list often far from the objects whose names are contained in it. An
object defined at one point in the file is repeated as a string literal in an __all__ list
somewhere else, usually at the top of the file.
Nothing keeps the two in sync, leading to these problems:
- Names get added to the module but never added to
__all__. - Names get removed from or renamed in the module but not in
__all__, sofrom spam import *raisesAttributeError. - It’s easy to typo a name (or leave out a list item delimiting comma) in
__all__. - Drift is only detectable in one direction. A linter can flag a name in
__all__that doesn’t exist as an object in the module, but no tool can flag a public name missing from__all__, because nothing in the source says that name was meant to be public. - Readers of the code must scroll to a different part of the file (or a different screen) to answer “is this name public?”
The convention of prefixing private names with an underscore addresses a related but different problem, and PEP 842 describes at length why prefixing is not by itself a sufficient answer.
The pattern proposed here – declaring visibility at the definition site with a decorator – is not
new or speculative. The atpublic package on PyPI has implemented it for a decade, and it is
already depended on by a number of projects. What this PEP proposes is that the pattern is common
enough, and useful enough, to be spelled without a third-party dependency. Thus it proposes to add
atpublic’s public() and private() functions to the builtins.
__all__ already defines the public API
It is sometimes said that Python has no way to express which names in a module are public and
which are private, and that __all__ is merely a convention governing from spam import *.
However, the language reference explicitly says:
The public names defined by a module are determined by checking the module’s namespace for a variable named__all__; if defined, it must be a sequence of strings which are names defined or imported by that module. […] The names given in__all__are all considered public and are required to exist. If__all__is not defined, the set of public names includes all names found in the module’s namespace which do not begin with an underscore character ('_').__all__should contain the entire public API. It is intended to avoid accidentally exporting items that are not part of the API (such as library modules which were imported and used within the module).
This PEP explicitly adopts the definition of “public” in the Python Language Reference. Following from this:
The concept already exists and is normative. “Public name” is a term the language reference
defines, and it defines it in terms of __all__. This is specification text, not folklore.
Python is not missing a way to say what is public; it has one, and it is documented.
Exhaustiveness is already the contract. “__all__ should contain the entire public API” is
unambiguous. A module whose __all__ lists only part of its public surface is not exercising
some alternative reading – it is out of conformance with what the reference says __all__ means.
The imported-module problem is already in scope. The reference names it outright: __all__
exists in part to avoid “accidentally exporting items that are not part of the API (such as library
modules which were imported and used within the module).” A module that declares __all__
accurately and imports argparse does not leak the name argparse as part of its public
API, with or without renaming the import to _argparse.
The gap, then, is not semantic but ergonomic. Python already specifies what it means for a name to
be public, and already recommends that __all__ say so exhaustively, while providing no
convenient way to keep that promise as a module evolves. It asks authors to maintain a list of
string literals by hand, in a different part of the file from the definitions, which is subject to
being quite error prone.
This PEP supplies the missing ergonomics. It does not redefine what it means to be “public”, or
introduce a second notion of visibility, or change what __all__ already means. It doesn’t try
to redefine what it means for a name to be exported. It does however make the documented contract
easy enough to actually honor.
Specification
Two new builtins are added: public() and private().
This PEP concerns module-level visibility only. public() and private() declare which of a
module’s global names make up its public interface, and they do so by maintaining __all__, which
is defined for modules and nothing else. Visibility in any other scope is explicitly excluded:
class attributes and methods, names local to a function, and names bound in nested scopes are all
untouched by this proposal. Python has no __all__ equivalent for those scopes, and this PEP
does not propose one. Whether a method is part of a class’s public interface remains, as today, a
matter of naming convention and documentation.
public()
public() has two call forms. The decorator form (@public) is the most common use.
Decorator form. When called with a single positional argument that has both a __module__ and
a __name__ attribute – i.e. a function or a class – public() appends that object’s
__name__ to the __all__ of the module in which public() is called, and returns the
object unchanged:
@public
def foo():
...
@public
class Bar:
...
# __all__ == ['foo', 'Bar']
Note that the bare decorator is used; Python’s semantics are to implicitly pass the object it decorates as the first argument to the decorator function.
Function call form. Names which cannot be decorated, such as constants, instances, and aliases,
are declared by calling public() with keyword arguments. Each keyword binds its value in the
calling module’s globals and appends the name to __all__:
public(SEVEN=7)
public(a_bar=Bar())
public(ONE=1, TWO=2)
The value of a single keyword argument is returned; for multiple keyword arguments, a tuple of the values is returned in order:
a, b, c = public(a=3, b=2, c=1)
d = public(d=9)
In all cases, public() modifies only the __all__ of the module in which it is called. No
other module’s __all__ is ever affected.
If the module does not already define __all__, public() creates it as an empty
list before appending. If __all__ exists but is not a list, ValueError is
raised. Any strings already present in an existing __all__ are left in the list. Appending is
idempotent, so a name that already appears in __all__ is not added a second time.
private()
private() (used exclusively as @private) is the dual of the decorator form of public().
It documents that a name is not part of the module’s public interface, and guarantees that the
name does not appear in __all__, removing it if it is already present. The decorated object is
returned unchanged:
@private
def helper():
...
Unlike public(), private() never creates __all__. If the module does not define
__all__, @private has no effect on the module namespace at all; it serves purely to
document the author’s intent at the point of definition. If __all__ does exist it must be a
list, or ValueError is raised, and the decorated object’s name is removed from it if present.
@private deliberately does not create an empty __all__, because doing so would silently
change the meaning of from spam import *. With no __all__, a wildcard import binds every
name not beginning with an underscore; with __all__ = [] it binds nothing. A decorator whose
purpose is documentation should not have that effect.
It follows that @private alone does not exclude a name from from spam import *. Excluding
names is the job of @public: as soon as any name in the module is marked public, __all__
exists, and everything not marked public is excluded automatically. @private records the
author’s intent; @public is what makes that intent observable.
Note
private() does not support a function call form, as no valid use case for it has been
identified or requested by users of the atpublic package. See Open Issues for further
discussion.
Restrictions
Because @public and @private exist to keep the __all__ module global in sync, only
module-level objects may be declared. Decorating a method inside a class body is not supported,
since __all__ documents module contents, not class contents.
Neither function inspects the scope it is called from, so this misuse is not currently diagnosed.
A decorator applied to a method appends the method’s name to the enclosing module’s __all__,
and a function call form used in a class body binds its keywords in the module globals rather than
in the class body. Neither outcome is likely to be what the author intended. Whether these cases
should raise an exception instead is an Open Issues question.
Because __all__ must be mutable for these functions to append to it, a module that assigns
__all__ itself must assign a list. A module that wants an immutable __all__ can freeze it
after the last declaration with __all__ = tuple(__all__).
Rationale
Why builtins?
The declaration of a module’s public interface is a fundamental and often requested property,
especially as code bases grow. Many use cases have been identified in discussions, and different
approaches have been developed in different libraries and applications. Enough experience has been
gained over the decade of atpublic’s existence that requiring a third-party dependency (or an
import at the top of every module) to spell something this fundamental is friction that discourages
its use. It is also awkward in exactly the places where it matters most: the standard library
itself, and small single-file modules.
atpublic acknowledges this today by offering an optional install step
(pip install atpublic[install]) that injects public and private into builtins at
interpreter startup, so that no import is needed. That this exists at all is evidence that
builtins is the most convenient location for these utilities.
Why decorators?
A decorator puts the declaration exactly where the definition is, which is the entire point.
The mechanical benefit is that the name appears only once. It cannot drift out of sync, refactoring tools rename it correctly for free, there is no second list to maintain, and the need to repeat yourself largely disappears.
The documentary benefit matters just as much. @public and @private record the author’s
intent on the line a reader is already looking at. Answering “is this part of the API?” takes no
scrolling to a list elsewhere in the file, no cross-checking that list against the definitions, and
no guessing about whether a leading underscore was deliberate. The declaration stops being
bookkeeping attached to the definition and becomes part of it.
@private demonstrates this most clearly. In a module with no __all__ it does nothing
mechanically at all: it adds no name, removes no name, and changes no behavior. Its entire value
is to say, at the point of definition, that the name is deliberately not public.
Static analysis of the function call form
The strongest objection to this proposal concerns the function call form, and it is worth stating explicitly. Given:
public(SEVEN=7)
SEVEN is bound in the module’s globals by a function that reaches into its caller’s frame.
Nothing about that binding is visible in the syntax tree. A type checker, linter, or language
server reading the source sees a bare function call and no assignment, and will therefore report
SEVEN as undefined at every use site. A soft keyword like export SEVEN = 7, as proposed by
PEP 842, has no such problem, because syntax is by construction visible to anything that parses
the file. This, and not the DRY objection raised in PEP 842, is the real cost of
choosing a builtin over a keyword.
This could easily be alleviated by future modifications to linting tools, so that they explicitly
recognize the function call form of public(). This would be a one-time, bounded cost paid by a
handful of tools, not an ongoing cost paid by every Python programmer.
public() is not an arbitrary function performing mysterious magic. It is a builtin with a
small, fixed, specified signature, and its effect on the module namespace is fully determined by the
keyword names at the call site, which are literally present in the source. Teaching a checker
that public(SEVEN=7) binds SEVEN and appends "SEVEN" to __all__ is a simple analysis
that these tools can easily perform.
There is direct precedent. Static analyzers already model __all__ mutation beyond simple
assignment, including __all__ += [...] and __all__.append(...), precisely because real code
does this. They already special-case namespace-creating callables whose behavior is not evident
from the grammar, such as namedtuple(), TypedDict, and
dataclass(). Adding public() to that list is an increment on work these
tools have already done, not a new category of problem.
If this PEP is accepted, that support is expected to follow quickly, for the ordinary reason that
tools support what the language provides. In the interim (and for older tool versions) the return
value of public() gives an entirely explicit spelling that requires no special support at all:
SEVEN = public(SEVEN=7)
Here the binding is a plain assignment, visible to every tool that parses Python. This form is a transition aid rather than the recommended spelling, and it should not be needed for long.
The conclusion is that the data and type alias use cases, which are the places a decorator
genuinely cannot be utilized, do not require new syntax at all. A function call that tools can
recognize serves just as well, without the need for a new, dedicated export keyword.
Is this urgent?
Guido van Rossum raised this question about PEP 842, and it applies with equal force here:
But Python has existed without this feature for over 35 years – is it really urgent? Remember the Zen of Python, which says “Now is better than never. Although never is often better than right now.”
No. This PEP is not urgent, and it does not claim to be. Nothing about module name visibility, or about a module’s exported public API, is urgent. But urgency is the wrong test to apply to this particular proposal, for three reasons.
The feature is not new. This PEP does not ask Python to adopt an untried idea; atpublic has
implemented these exact semantics since 2016. The question is not “should Python have this?” since
users who want it already have it, but “should having it cost a third-party dependency?” A decade
of production use is the opposite of rushing. It has already surfaced and settled the corner cases,
syntax, and semantics a fresh design would have to guess at: that only module-level objects can be
decorated, what to do about a non-list __all__, and what the function call form should return.
The cost of being wrong is low. The urgency argument has the most weight against changes that cannot be walked back. Syntax is permanent: a soft keyword constrains the grammar forever, must be taught to every future Python programmer, and is unavailable to any module supporting an older interpreter. A new module-level variable with runtime consequences changes the observable behavior of code without warning. A builtin function is the cheapest thing in this design space on both counts: it is inert until called, it changes nothing about modules that ignore it, and if it proves to be a mistake it can be deprecated in the ordinary way without touching the grammar.
The sequencing matters more than the timing. Three proposals in this cycle address the same
problem space, and two of them ask for new syntax. If Python is going to change its grammar to
address this need, that decision should be made after weighing the option that requires no grammar
change, not before. Once an export keyword exists, builtins covering the same ground are
redundant and will never be added, regardless of whether they were the better answer. That
asymmetry is the reason to consider this PEP now rather than later: not because the feature is
pressing, but because the cheaper alternative stops being available once the expensive one lands.
Import-time performance
When this idea was informally floated with core developers some years ago, before either PEP 842 or PEP 843 existed, the objection raised was not the design but the cost weighed against its utility: a decorator runs at import time, once per decorated name, and CPython’s startup time is a closely watched number. The concern is legitimate and deserves a direct answer.
The work per call is small and bounded. public() in decorator form reads the decorated
object’s __name__, obtains the defining module’s globals, creates __all__ as an empty list
if needed, and appends one string. There is no complicated introspection, no allocation or work
proportional to module size, and no I/O. Whatever the constant factor turns out to be, it does not
grow with the size of the module.
The cost is opt-in and proportional to the public API. A module that does not call public()
pays nothing at all, unlike a change to module attribute access, which affects every module whether
or not it participates. A module that does call it pays once per public name, and a module’s
public surface is typically a small fraction of the names it defines.
Syntax is not free either. It is worth being precise about what the alternative saves.
PEP 842’s export statement is specified to check that the name exists in globals, create
__export__ if absent, and call list.append – the same operations, expressed in bytecode
rather than a call. The saving is the function call dispatch, not the underlying work. That is a
real difference, but it is a constant factor on an already small constant, not a difference in kind.
A C implementation is feasible and fast. This is the point on which a builtin is strictly better
positioned than the third-party package. atpublic shipped a C implementation of public()
for a time, and it was substantially faster than the pure Python version. It was ultimately
dropped, not because it did not work, but because requiring a compiled extension module in a
third-party package is a significant packaging and installation burden for a library this small
– a burden borne entirely so that the pure Python fallback could be avoided.
That trade-off does not exist in CPython. A builtin is compiled as part of the interpreter, so the
fast implementation is simply the implementation, with no wheel platform support matrix, no
fallback path, and no optional extra. Moreover, a C implementation inside the interpreter can do
less work than any third-party one: the decorator form can access the calling frame’s globals
directly, rather than the __module__ plus sys.modules lookup a pure Python
implementation requires, and the function call form needs no Python-level stack inspection.
The argument is therefore somewhat the reverse of the original objection. The performance concern
is a reason to put public() in builtins where it can be made fast, rather than a reason to leave
it on PyPI, where it cannot.
Note
This section argues that the cost is acceptable; it does not yet demonstrate it. Measurements against CPython’s startup benchmarks, for both a decorated standard library and a synthetic worst case, should accompany the reference implementation. See Open Issues.
Relationship to PEP 842 and PEP 843
In brief: PEP 842, in its current revision, proposes adding an export keyword and a new
module global __export__ variable. PEP 843 proposes adding a from ... export ... form.
Two problems, not one
Discussion of module visibility addresses two separable problems:
- Bookkeeping. A name’s visibility as public or private is declared in a different place from where the object so named is defined, so the declaration drifts out of sync with the implementation. This is a problem about where you add the declaration.
- Runtime consequences.
__all__declares the public API, but the only place that declaration is enforced isfrom spam import *. It has no effect on attribute access,dir(),help(), or autocompletion, so a name that is intended to be kept private is indistinguishable from public names to these patterns of module introspection. This is a problem about what the declaration does.
This PEP addresses only the first. It takes the position that the first problem is the more pressing and the more broadly applicable of the two, that it can be solved without new syntax and without a new variable, and that solving it does not commit Python to any particular answer to the second.
Why __all__ and not __export__
PEP 842 proposes a new __export__ variable. This PEP proposes to keep using __all__.
PEP 842 gives two reasons why __all__ is inadequate. The first is that __all__ drifts
out of sync with the module. That is true, and it is precisely the problem atpublic and this
PEP solve. However, a new list of string literals in the same distant part of the file does not
directly solve this problem. PEP 842’s own revision history concedes the point, quoting Guido
van Rossum on the original __export__-only design:
But the ergonomics are similar to those of__all__, and those are bad. It’s too easy to forget to add (or remove!) something to the list, and it’s distracting to have to update the export info in a totally different part of a file than the definition of the exported thing.If we just cared about classes and functions, a more ergonomic approach would be an
@exportdecorator. If we also care about exporting data or type aliases, I’d much rather look for a solution that adds a soft keyword namedexport(orprivate, for a better default).
Drift is a property of declaring at a distance, not a property of __all__. Any variable
maintained by hand has it, and no variable maintained at the definition site does.
The first half of that quote is the argument this PEP is built on, and the second half names the decorator as the ergonomic answer for classes and functions. The remaining question – what to do about data and type aliases, where there is nothing to decorate – is addressed in Static analysis of the function call form.
The second reason is that __all__ is not always exhaustive in practice. A module may
deliberately keep a public type alias out of __all__ to avoid polluting wildcard-importing
namespaces, so its public API can end up a superset of what __all__ lists.
That is an accurate observation about existing code, but it is a weaker argument than it first
appears, because it describes a deviation from the specification rather than an alternative
reading of it. As __all__ already defines the public API sets out, the language reference already states
that __all__ “should contain the entire public API.” A module that withholds public names from
__all__ is not asserting that __all__ means something narrower than the public API; it is
trading conformance away for control over import *.
What that trade exposes is a real flaw, but a different one from the one PEP 842 diagnoses:
__all__ does double duty. It is at once the declaration of what is public and the control
surface for wildcard imports, and when those two purposes conflict, authors sacrifice the
declaration because only the wildcard behavior has any teeth.
Introducing __export__ does not repair that conflation. It leaves __all__ doing both jobs,
adds a second declaration to keep synchronized with the first, and transfers the word “public” to
the new module variable, while the language reference continues to define it in terms of
__all__. A module conscientious enough to maintain __export__ accurately would have been
conscientious enough to maintain __all__ accurately; the ones that drift will drift in both.
This PEP takes no position on whether unexported-name warnings are desirable. It observes only that
the bookkeeping question is separable from the runtime-semantics question, and it answers the
former. public() populates a list; if Python later decides that some list should carry runtime
consequences, public() can populate that one instead, or both. Nothing here closes the door on
PEP 842.
Why PEP 843 is a good companion
This PEP does not solve the DRY problem for re-exports, and cannot do so gracefully. A “hub module” that pulls names out of private submodules must currently write each name three times:
from ._core import Widget
public(Widget=Widget)
Widget is named once to import it, and twice more to export it. That’s a big violation of DRY!
Hand-maintaining __all__ would name it only twice, so for re-exports specifically, public()
is not merely unhelpful, but a step backwards.
The decorator form of @public is unavailable here because there is nothing to decorate, and the
function call form of public() requires naming the binding explicitly. This is exactly the gap
PEP 843 identifies, and its from ._core export Widget spelling closes it in a way no decorator
can.
The two proposals therefore partition the problem cleanly, and provide excellent synergy:
public()andprivate()handle the names a module defines.from <module> export <name>handles the names a module passes through.
Both populate __all__. Neither requires the other, and neither requires new runtime semantics
for the result.
Note
PEP 843 was published as this PEP was being drafted, and PEP 842 has since grown an export
statement of its own that overlaps both this PEP and PEP 843. The relationship between all three
needs to be settled on the discussion thread; see Open Issues.
Backwards Compatibility
Adding names to builtins shadows nothing, but it does mean that modules which define their
own module-level public or private names will shadow the builtins instead. This is the
same situation as any other builtin (id, type, list), and is well understood.
Code that imports public and private from the atpublic package will continue to work
unchanged (as long as the semantics continue to match), since an explicit import shadows the
builtin.
Code that already uses public or private as a variable or parameter name will begin to trip
linters that flag shadowed builtins, such as flake8-builtins and the equivalent ruff rule.
This is a diagnostic change rather than a behavioral one, and the same has been true of every
builtin added to Python. How much existing code this affects has not been measured.
Modules using these builtins will not run on Python 3.15 and earlier without either a dependency on
atpublic or a compatibility shim.
Security Implications
This PEP has no known security implications. Like __all__ itself, public() and
private() are documentation, not access control.
How to Teach This
public() and private() would be documented alongside the other builtins, and referenced from
the tutorial section on modules where __all__ is introduced.
The rule to teach is a single sentence: decorate a name with @public if users of your module are
meant to use it, and don’t decorate it (or decorate it with @private, to say so explicitly) if
they aren’t.
Constants and other names that cannot be decorated use the function call form, which both binds the name and marks it public:
public(SEVEN=7)
This replaces the assignment rather than accompanying it. Writing SEVEN = 7 as well would
define the name twice, which is the repetition these builtins exist to remove.
Adoption can be incremental. A module with a hand-written __all__ can start decorating
definitions without removing it, because names already listed are not added twice, and the two
styles can coexist indefinitely.
Reference Implementation
The atpublic package, available on PyPI and maintained since 2016, implements the proposed semantics in pure Python. Its source repository is hosted on GitLab.
A CPython implementation has not yet been written.
For a time, atpublic also included a C implementation of public(), which was considerably
faster than the pure Python one. It was dropped for packaging reasons that do not apply to a
builtin. See Import-time performance.
One divergence is worth noting. atpublic 7.0.0 and earlier create __all__ in the
@private case, contrary to the specification above. This was identified as a bug while drafting
this PEP, and will be corrected in atpublic 8.0.0, which is in pre-release at the time of this
writing.
Rejected Ideas
New export syntax instead of decorators
PEP 842, in its current revision, proposes an export soft keyword covering the same ground as
this PEP – export def, export class, export NAME = value. Its
Rejected Ideas section considers builtin public and private
decorators, describes them as the author’s next preferred alternative to syntax, and rejects them on
the grounds that “there’s no easy way to export simple variables without duplicating the name.”
That objection doesn’t fully apply to the design proposed here. The function call form exists precisely for the undecoratable cases, and writes the name exactly once:
public(SEVEN=7)
is the whole declaration. The name SEVEN is bound to 7 in the module globals, and
"SEVEN" is appended to __all__. There is no separate assignment to keep in sync. Compare
export SEVEN = 7: the two spellings carry the same information, cost roughly the same
keystrokes, and differ only in that one of them requires a grammar change.
The substantive version of the objection is not about keystrokes but about tooling: a soft keyword is visible to static analysis, while a function call that binds through its caller’s frame is not. That is a real cost, and it is answered in Static analysis of the function call form.
The general argument holds beyond this example. New syntax is the most expensive thing Python can add: it must be taught, it cannot be back-ported, it constrains the grammar permanently, and it is unavailable to every module that must still run on an older interpreter. A builtin costs none of that, is trivially shimmed on old versions, and (as is the case here) has a decade of usage experience behind it.
Add a new __export__ variable
Leave it on PyPI
Leaving atpublic on PyPI is the status quo option. Users who want to opt into this
functionality can simply add that library as a dependency and import the functions (or use the
pip install atpublic[install] extra to populate builtins).
However, if this is a problem worth solving now, then leaving this in a third-party package on PyPI doesn’t serve our users adequately. The need to include a dependency and an explicit import may be just enough of a hurdle (albeit small) to stop widespread use of it. Adding it to builtins endorses the pattern in a way that should broaden its adoption.
A new standard library module instead of builtins
This would eliminate the third-party dependency problem, but still leaves the explicit import
usability cost. In addition, there’s no obvious place to add it to the stdlib other than in
builtins. Two functions likely aren’t worth the cost of a new top-level module. Besides, since
__all__ is in a sense built into Python, these functions should be built in too.
Open Issues
- How should this PEP, PEP 842, and PEP 843 be reconciled? All three now contain a definition-site or re-export declaration mechanism, and the overlap needs to be resolved before any of them can sensibly be accepted.
- Should
populate_all(),atpublic’s heuristic “infer__all__from what’s defined here” function, also be included? This is deferred for now; a heuristic is a harder case to make for a builtin than the two explicit declarations are, and is less essential for improving module visibility ergonomics. - Should
private()support a function call form, for symmetry?atpublicdoes not provide one and no need for it has ever been demonstrated or requested. - Should
public()andprivate()diagnose being called outside module scope? Neither inspects its calling scope today, so@publicon a method silently adds the method’s name to the module’s__all__. Raising an exception would be friendlier, at the cost of a scope check on every call, which bears on Import-time performance. - Should the standard library itself adopt these decorators, and if so on what schedule? This question is entangled with Import-time performance and should be settled with startup measurements in hand. Also, as with all new capabilities (such as lazy imports), Python’s policy is generally not to wholesale update the stdlib to embrace the new functionality. These new functions can be utilized opportunistically in modules where the most benefit can be gained, or when a module undergoes substantial rewrite.
- Import-time benchmarks for a C implementation are outstanding.
Acknowledgements
Thanks to Peter Bierma and Neil Girdhar, whose PEP 842 and PEP 843 prompted this proposal, and to
the contributors to and users of atpublic over the past decade.
Change History
TBD
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.