Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 844 – public and private builtins

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

Table of Contents

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__, so from spam import * raises AttributeError.
  • 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:

  1. 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.
  2. Runtime consequences. __all__ declares the public API, but the only place that declaration is enforced is from 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 @export decorator. If we also care about exporting data or type aliases, I’d much rather look for a solution that adds a soft keyword named export (or private, 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() and private() 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

See Why __all__ and not __export__.

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? atpublic does not provide one and no need for it has ever been demonstrated or requested.
  • Should public() and private() diagnose being called outside module scope? Neither inspects its calling scope today, so @public on 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