From 6f50b3420cfb48c7e8074d4f425ee06fd8d5e53d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 11 Jul 2026 22:48:21 +0300 Subject: [PATCH 1/5] PEP 837: Extensible JSON serialization Co-Authored-By: Claude Fable 5 --- .github/CODEOWNERS | 1 + peps/pep-0837.rst | 720 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 721 insertions(+) create mode 100644 peps/pep-0837.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 509e23f3e1e..4a2af7e19a3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -711,6 +711,7 @@ peps/pep-0832.rst @brettcannon peps/pep-0833.rst @dstufft peps/pep-0835.rst @ilevkivskyi peps/pep-0836.rst @savannahostrowski @Fidget-Spinner @brandtbucher +peps/pep-0837.rst @serhiy-storchaka # ... peps/pep-2026.rst @hugovk # ... diff --git a/peps/pep-0837.rst b/peps/pep-0837.rst new file mode 100644 index 00000000000..97a79317e67 --- /dev/null +++ b/peps/pep-0837.rst @@ -0,0 +1,720 @@ +PEP: 837 +Title: Extensible JSON serialization +Author: Serhiy Storchaka +Status: Draft +Type: Standards Track +Created: 11-Jul-2026 +Python-Version: 3.16 + + +Abstract +======== + +This PEP adds an extension mechanism to the :mod:`json` encoder +consisting of three complementary parts, one per level of +customization: + +* **library level**: a serialization protocol — the special methods + ``__json__()`` and ``__raw_json__()``; + +* **application level**: a global registry — + ``copyreg.json(cls, function)`` filling + ``copyreg.json_dispatch_table``, following the precedent of + :func:`copyreg.pickle`; + +* **call level**: a per-encoder dispatch table — a ``dispatch_table`` + attribute on a :class:`json.JSONEncoder`, mirroring + :attr:`pickle.Pickler.dispatch_table`. + +The more specific level takes precedence. + +A helper class ``copyreg.RawJSON`` wraps an already-encoded JSON string +so that it is emitted verbatim, enabling representations that are not +otherwise expressible (such as serializing :class:`decimal.Decimal` as a +JSON number with full precision). + +Several standard library container types whose JSON form is unambiguous — +:class:`collections.deque`, :class:`types.MappingProxyType`, +:class:`collections.ChainMap`, :class:`collections.UserDict`, +:class:`collections.UserList` and :class:`collections.UserString` — +gain a ``__json__`` method and therefore serialize out of the box. + +The :mod:`json` module itself gains no new public names. + + +Motivation +========== + +Serializing anything beyond the basic types (``dict``, ``frozendict``, +``list``, ``tuple``, ``str``, ``int``, ``float``, ``bool``, ``None``) +with :func:`json.dumps` today requires either passing a ``default=`` +function to every call, or subclassing :class:`json.JSONEncoder` and +threading the subclass through every call site. Both mechanisms attach +the knowledge to the *call* rather than to the *type*: + +* **They do not compose.** Two libraries that each need a custom + ``default=`` cannot both have it applied to one document without the + application writing a merging wrapper by hand. A library that + serializes internally (for logging, caching, IPC) cannot see the + application's ``default=`` at all. + +* **Third-party types cannot opt in.** A library defining a new type + has no way to make it JSON-serializable for its users; every user must + learn and repeat the incantation. Requests to fix this for NumPy + scalars and arrays (`python/cpython#68501`_, `python/cpython#62503`_) and + for D-Bus integer types (`python/cpython#77978`_) were closed as + out of scope for the standard library — correctly, but leaving the + underlying need unmet. + +* **Some representations are impossible.** ``default=`` must return one + of the basic types, so there is no way to emit a non-integer JSON + *number* that ``float`` cannot represent exactly. :func:`json.loads` + can read decimal numbers losslessly via + ``parse_float=decimal.Decimal``, but the result cannot be written + back (`python/cpython#67312`_). The same limitation blocks control over float + formatting (`python/cpython#81022`_) and non-finite float representation + (`python/cpython#98306`_, `python/cpython#134717`_), and the general request + for emitting pre-encoded JSON (`python/cpython#86957`_). + +The demand is long-standing and broad. Beyond the general proposals for +a serialization hook checked before raising :exc:`TypeError` +(`python/cpython#71549`_ — open since 2016, `python/cpython#79292`_, +`python/cpython#114285`_, `python/cpython#86931`_), the tracker accumulated +requests for out-of-the-box support of concrete standard library types: +:class:`decimal.Decimal` (`python/cpython#67312`_, `python/cpython#118810`_, +`python/cpython#145115`_), :class:`array.array` (`python/cpython#70451`_), +:class:`collections.deque` (`python/cpython#64973`_, `python/cpython#73849`_), +:class:`types.MappingProxyType` (`python/cpython#79039`_), +:func:`collections.namedtuple` as an object (`python/cpython#67661`_), and +:class:`datetime.datetime` (`python/cpython#65742`_); and for whole +categories: sets, frozensets, bytearrays and iterators +(`python/cpython#75338`_), generators (`python/cpython#78031`_), and +dictionary views (`python/cpython#83377`_). + +The idea has also been proposed independently for sixteen years, each +time reinventing part of this design: a ``__json__()`` method on +python-ideas in `July 2010`_ +and `April 2020`_ +and on `discuss.python.org in 2022–2024`_; +raw output and a standardized encoder protocol on `python-ideas in 2019`_; +a registration function on `discuss.python.org in 2022`_. + +Meanwhile the ecosystem adopted the convention piecemeal. `TurboGears`_ +serializes objects via a no-argument ``__json__()`` — and its +``custom_encoders`` configuration takes precedence over it, the same +ordering as this PEP. `Pyramid's JSON renderer`_ +calls ``__json__(request)``. Applications and libraries such as conda, +Checkmk and TatSu define ``__json__`` today. `simplejson`_ added an opt-in ``for_json()`` +method instead — choosing that name precisely because dunder names are +reserved for the language. Only the standard library can define +``__json__``; this PEP does, ending the fragmentation. + +The type-support requests could not simply be granted one by one. For +most of the requested types the JSON representation is *ambiguous*: + +* ``Decimal`` — a JSON number (``1234567890.0987654321``) or a JSON + string (``"1234567890.0987654321"``)? Both are common; each is wrong + for some consumers. +* ``namedtuple`` — a JSON Array (it is a tuple) or a JSON Object (it has + named fields)? +* ``array.array`` — a JSON Array of numbers, or a compact encoded + string? The sensible answer even depends on its type code. +* ``datetime`` — which of the many string representations (ISO 8601 + variants, epoch seconds, RFC formats)? + +No standard library default can be correct, so the standard library +should provide the *mechanism* by which each application declares its +*policy*. That mechanism is this PEP. + + +Rationale +========= + +One system covering all of the collected needs +---------------------------------------------- + +The feature is deliberately a small *system* rather than a single hook: +each part answers a different cluster of the requests above, and no +part alone covers them all. + +* The ``__json__`` **protocol** answers the general extension proposals + and the third-party-type reports: the type's author makes it + serializable once, with no imports, inherited by subclasses. + +* The **registry** answers the requests for types the requester does + not control or whose representation is a policy choice (``Decimal``, + ``namedtuple``, ``array.array``, ``datetime``): the application + declares its policy in one line. + +* **Raw output** (``__raw_json__``/``RawJSON``) answers the requests + for representations that no basic-type conversion can express: + pre-encoded fragments, full-precision numbers, float formatting, + non-finite floats. + +* **Duck typing of hook results** answers the category requests + (iterables, sets, generators, dictionary views, mappings): + ``copyreg.json(set, sorted)`` is a complete solution for sets, and a + hook may return any iterable or mapping-like object rather than + materializing a list or dict. + +* **Standard library** ``__json__`` **methods** answer the requests for + unambiguous containers (``deque``, ``mappingproxy``) directly. + +* The **per-encoder** ``dispatch_table`` scopes any of the above to one + encoder when a process needs different policies for different + outputs. + +Three levels of customization +----------------------------- + +The three parts correspond to the three levels at which serialization +is decided, and deliberately have different shapes: + +* **Library level** — ``__json__()`` is for the author of a type. A + method needs no imports and is inherited by subclasses: a library + can make its types JSON-serializable without depending on + :mod:`json` or :mod:`copyreg` at all. + +* **Application level** — ``copyreg.json()`` is for users of a type + they do not control: the application chooses, process-wide, how + third-party or ambiguous standard library types serialize. + Registration is by exact type; subclasses that want the same + treatment inherit ``__json__`` instead. + +* **Call level** — the ``dispatch_table`` attribute on a + :class:`json.JSONEncoder` subclass or instance customizes a + particular call, when one program needs different policies for + different outputs (an external API versus an internal cache, say). + The existing ``default=`` parameter remains the call-level catch-all + for objects no other mechanism handled. + +The more specific level takes precedence: a registry entry is +consulted before the type's ``__json__`` (the application overrides +the library), and a per-encoder table replaces the global registry +(the call overrides the application). + +Why the registry lives in copyreg +--------------------------------- + +The registry could have been ``json.register()``. It is placed in +:mod:`copyreg` instead, for three reasons. + +**Import cost and layering.** A library that registers a serialization +function for a foreign type at import time should not pay for the +:mod:`json` module, whose import is dominated by :mod:`re` (roughly +fifty times the cost of importing :mod:`copyreg`, which is imported by +:mod:`copy` and :mod:`pickle` anyway). With the registry and +``RawJSON`` both in :mod:`copyreg`, a registrant imports exactly one +small module. The :mod:`json` module imports :mod:`copyreg` (as +:mod:`copy` already does), never the reverse. + +**Precedent and symmetry.** This is the pickle model, mirrored in both +halves: a registration function filling a global table +(:func:`copyreg.pickle` / ``copyreg.json()``) and a per-instance +``dispatch_table`` attribute overriding it (:class:`pickle.Pickler` / +:class:`json.JSONEncoder`). The function name follows the +``copyreg.pickle()`` convention; thirty years of that precedent show +that a registration function named after its protocol causes no +confusion, because registration is a one-off, module-prefixed call. + +**Neutral ground.** Third-party JSON encoders can honor the same +registry and helper class without importing the standard :mod:`json` +module, so the ecosystem can converge on a single registration point. + +Placing the registry in :mod:`copyreg` also generalizes the module's +charter — registering protocol implementations for types you do not +control. Registries for other protocols (such as ``__copy__``- and +``__deepcopy__``-like hooks for the :mod:`copy` module) fit the same +pattern and will be proposed separately. + +The raw-output mechanism +------------------------ + +Emitting an already-encoded fragment verbatim is the only way to express +representations outside the basic types, most importantly full-precision +numbers. The chosen mechanism is a duck-typed special method, +``__raw_json__()``, with ``copyreg.RawJSON`` as a convenience wrapper: + +* A hook (or ``__json__``) that wants raw output returns + ``copyreg.RawJSON(fragment)`` — one import, which the registrant has + already paid — or an instance of its own wrapper class defining just + ``__raw_json__``, with no imports at all. + +* ``RawJSON`` additionally defines ``__json__`` returning ``self`` to + serve its second function: including a pre-serialized fragment + *directly* in a document (as a value in a dict or list passed to + :func:`json.dumps`), not only as a hook result. + +The protocol, not the class, is the interface; the class is sugar. + +Performance: the encoding pipeline as an invariant +-------------------------------------------------- + +The :mod:`json` encoder is among the hottest code in the standard +library, and the design treats its branch order as an invariant: + +1. Objects of the exact basic types are encoded with **no attribute + lookups at all** — the overwhelmingly common case pays nothing for + this PEP. + +2. The dispatch table and ``__json__`` are consulted next — one dict + lookup plus one type-attribute lookup — *before* the ``isinstance`` + fallbacks, solely so that subclasses of the basic types can + customize their serialization at all (an ``int`` subclass would + otherwise be consumed by the ``isinstance(o, int)`` check, the very + problem history records for ``IntEnum``: `python/cpython#62464`_). + +3. The ``isinstance`` fallbacks then catch non-customizing subclasses, + preserving today's behavior for them exactly (``IntEnum`` still + serializes as a number). + +4. Duck typing of a hook result — ``__index__`` as a number, + ``__float__`` as a number, ``__iter__`` with ``keys()`` as an + object, ``__iter__`` alone as an array, ``__raw_json__`` verbatim — + applies **only when a hook has fired**. In particular, + ``__raw_json__`` is never looked up on an object that did not come + from a hook, so objects bound for the subclass checks or + ``default()`` pay no extra lookups. + +5. The ``default`` hook runs last, unchanged. + +Which stdlib types get a default __json__ +----------------------------------------- + +The criterion: **a standard library type gets a default ``__json__`` +only when its JSON form is unambiguous.** Transparent containers +qualify — ``deque``, ``mappingproxy``, ``ChainMap``, ``UserDict``, +``UserList``, ``UserString`` are semantically just arrays and objects +(resolving `python/cpython#64973`_, `python/cpython#73849`_ and +`python/cpython#79039`_ directly). ``Decimal``, ``namedtuple``, +``array.array`` and ``datetime`` do not qualify, for the ambiguity +reasons given in the Motivation; for them, this PEP's answer is a +one-line registration by the application (see `How to Teach This`_). + +This criterion is also the ready-made answer to future "just add +``__json__`` to X" requests. + + +Specification +============= + +The __json__ protocol +--------------------- + +A class may define a method ``__json__(self)``. When the :mod:`json` +encoder encounters an object that is not of an exactly-supported basic +type and has no dispatch-table entry, it looks up ``__json__`` on the +object's type (like all special methods) and, if present, calls it. +The result is then serialized in place of the object: + +* A result of a basic type, or an instance of their subclasses, is + serialized as usual. + +* Otherwise the result is interpreted by duck typing: an object with + ``__index__`` is serialized as a JSON number via + :func:`operator.index`; else one with ``__float__`` as a JSON number + via :class:`float`; else an iterable with a ``keys()`` method (and + ``__getitem__``) as a JSON object; else an iterable as a JSON array; + else an object whose type defines ``__raw_json__`` is serialized by + calling it (see below). Otherwise :exc:`TypeError` is raised. + +The result of ``__json__`` is not recursively re-submitted to the +protocol: a result that is itself of a hook-defining type is not +converted again. + +The __raw_json__ protocol +------------------------- + +``__raw_json__(self)`` must return a :class:`str`, which the encoder +emits verbatim, without validation of its content. It is consulted +only for objects produced by a dispatch-table function or ``__json__`` +(see the pipeline invariant above), so a wrapper class used as a hook +result needs only ``__raw_json__``. ``RawJSON`` also defines +``__json__`` returning ``self``, which lets its instances appear +directly in the input document. + +The registry +------------ + +The following are added to :mod:`copyreg`: + +``copyreg.json(ob_type, json_function)`` + The official registration interface. Registers ``json_function`` + as the serialization function for ``ob_type``. Raises + :exc:`TypeError` if ``json_function`` is not callable. The + function is called with the object as its only argument and its + result is interpreted exactly like the result of ``__json__``. + Registration is by exact type: it does not apply to subclasses of + ``ob_type``. + +``copyreg.json_dispatch_table`` + The dict mapping types to registered functions, consulted by the + :mod:`json` module (and available to other JSON encoders honoring + the registry). Applications register via ``copyreg.json()`` rather + than mutating the table directly. + +``copyreg.RawJSON(encoded_json)`` + A wrapper whose instances are serialized by emitting + ``encoded_json`` verbatim. ``str()`` of the instance returns the + fragment. + +Registered functions take precedence over ``__json__``. Entries for +the exactly-supported basic types themselves (``dict``, ``list``, +``str``, ``int``, ...) are never consulted, because the fast paths for +those types run first. + +The encoder +----------- + +:class:`json.JSONEncoder` (and the C accelerator) consult +``getattr(encoder, 'dispatch_table', copyreg.json_dispatch_table)``, so +a subclass may set a ``dispatch_table`` class or instance attribute to +replace the global registry for that encoder, mirroring +:class:`pickle.Pickler`. + +Dict *keys* are not affected by any part of this PEP: key serialization +accepts exactly the types it accepts today, and neither the dispatch +table nor ``__json__`` is consulted for keys (see Open Issues). + +The ``check_circular`` machinery is unchanged: containers and +``default`` results are tracked as today. Hook results are not +separately tracked; a pathological hook that returns a fresh cycle on +every call ends in :exc:`RecursionError` like any runaway recursion. + +Standard library __json__ methods +--------------------------------- + +``__json__`` returning a view of the obvious container is added to: +:class:`collections.deque` and :class:`types.MappingProxyType` (in C, +returning ``self``; the encoder consumes them via the array and object +paths), and :class:`collections.ChainMap`, +:class:`collections.UserDict`, :class:`collections.UserList`, +:class:`collections.UserString` (in Python, returning ``self`` or the +underlying ``data``). + +Backwards Compatibility +======================= + +The :mod:`json` module gains no new public names and no existing +behavior changes for objects that define none of the new hooks: + +* All exactly-supported basic types serialize byte-for-byte as before. + +* Subclasses of basic types without hooks serialize as before + (``IntEnum`` as a number, str subclasses as strings, dict and list + subclasses as objects and arrays). + +* Objects previously rejected with :exc:`TypeError` that define + ``__json__`` will now serialize. Classes following the TurboGears + convention (a no-argument ``__json__``) get exactly the behavior + they intended. Classes written for Pyramid's renderer, whose + ``__json__(self, request)`` takes an argument, keep raising + :exc:`TypeError` — now from the missing argument rather than from + ``default`` — so code catching the exception keeps working, though + the message changes. A class using the name for something unrelated + would change behavior, but dunder names are reserved by the language + reference, and no plausible unrelated meaning is known. + +* The signature of the private ``_json.make_encoder`` gains a + ``dispatch_table`` argument. + +* The six standard library classes gaining ``__json__`` previously + raised :exc:`TypeError` under :func:`json.dumps` (unless handled by + ``default=``). Code using ``default=`` to serialize them keeps + working, because those objects no longer reach ``default`` — but the + built-in representation (array/object) may differ from what a custom + ``default`` produced. This is the usual risk of any new default + behavior and is judged acceptable for unambiguous containers. + +* ``from copyreg import json`` shadows the :mod:`json` module name in + the importing namespace, as ``from copyreg import pickle`` always + has. Documentation will show module-prefixed calls. + + +Security Implications +===================== + +``__raw_json__`` and ``RawJSON`` emit strings without validation, so a +hook returning attacker-controlled fragments could produce invalid or +misleading JSON. This is no new capability: ``default=`` already +executes arbitrary code during encoding, and producing malformed output +requires the application to have installed the hook. The documentation +will note that raw fragments must come from trusted producers. + + +How to Teach This +================= + +Four recipes, one per customization level: + +1. *Library level* — your own class: define ``__json__``:: + + class Money: + def __json__(self): + return {"amount": str(self.amount), + "currency": self.currency} + +2. *Application level* — someone else's class: register it:: + + import copyreg, decimal + copyreg.json(decimal.Decimal, str) + +3. *Application level* — a representation JSON cannot otherwise + express: return a raw fragment:: + + copyreg.json(decimal.Decimal, + lambda d: copyreg.RawJSON(str(d))) + +4. *Call level* — one encoder, different policy:: + + class APIEncoder(json.JSONEncoder): + dispatch_table = {decimal.Decimal: str} + +The existing ``default=`` remains the call-level catch-all for objects +no other mechanism handled and is documented as running last. + + +Reference Implementation +======================== + +Branch ``json-customize4`` (three commits), a rebase and rework of the +author's 2017–2022 ``json-customize`` branches: the protocol and +registry with C and Python encoder support, standard library +``__json__`` methods, and tests for both encoder implementations, +including the encoding-pipeline invariant recorded as code comments. + + +Rejected Ideas +============== + +``json.register()`` instead of copyreg + Registration in the :mod:`json` module forces registrants to import + it (~50× the import cost of :mod:`copyreg`, dominated by :mod:`re`), + breaks the pickle symmetry, and gives third-party encoders no + neutral registry. Discoverability is addressed by documentation in + the :mod:`json` docs pointing to :mod:`copyreg`. + +``copyreg.register_json()``-style names + Inconsistent with :func:`copyreg.pickle`, the thirty-year precedent. + +Default ``__json__`` for Decimal, namedtuple, array.array, datetime + Each has at least two legitimate JSON representations (number vs + string; array vs object; array vs encoded string, depending on type + code; many string formats). A default would be wrong for a large + fraction of consumers; the ambiguity is the argument for a registry, + not for a default. + +Serializing all iterables and mappings without opt-in + Requested in `python/cpython#75338`_ and implied by + `python/cpython#78031`_ and `python/cpython#83377`_, but a silent behavior + change: every object with ``__iter__`` (sets, generators, file + objects) would begin serializing as an array instead of reaching + ``default=`` or raising. Duck typing therefore applies only to hook + results — an explicit opt-in. + +``isinstance(o, RawJSON)`` instead of ``__raw_json__`` + Rejected because it taxes the wrong audience: a type that opts in + via ``__json__`` with zero imports would need to import + :mod:`copyreg` the moment it needs raw output. With the duck-typed + protocol, the class *is* the convenience, not the mechanism. + +Recognizing raw wrappers by class name + Dispatching on names has precedent in pickle, which recognizes the + ``__newobj__`` and ``__newobj_ex__`` callables in reduce tuples by + their ``__name__``, but this PEP does not follow that example. + Recognizing raw wrappers by ``type(o).__name__ == "RawJSON"`` would + inspect an ordinary, plausible class name on arbitrary objects: any + unrelated class that happens to use it would silently change + encoding behavior. The duck-typed ``__raw_json__`` keeps the + marker in the reserved dunder namespace. + +Consulting ``__raw_json__`` directly on all objects + Would let a class self-serialize raw with a single method, but adds + a type-attribute lookup for every object on the way to the subclass + checks, to the duck-typed interpretations (``__index__``, + ``__iter__``, etc.) and to ``default()`` — a real cost in one of + the hottest paths in the standard library. The gated design makes + the raw object pay one method call instead. + +Reusing ``__repr__``/``__str__`` or a generic ``__serialize__`` + Proposed in `python/cpython#114285`_. These methods cannot serve as + the *marker* for raw output: nearly every type defines them (and + their results are usually not valid JSON), so the encoder could not + tell raw-capable objects apart; a format-agnostic ``__serialize__`` + likewise cannot say *which* format it produces. ``__str__`` can, + however, serve as the *payload* once a separate marker exists — see + Open Issues. + +MRO-based (isinstance) dispatch for the registry + The registry uses exact-type matching like + ``copyreg.dispatch_table``. Subclass dispatch is the protocol's + job: a base class defines ``__json__`` once and subclasses inherit + it. Exact matching keeps lookup one dict access and avoids MRO + scans on the hot path. + + +Open Issues +=========== + +* **Dict keys.** Neither the registry nor ``__json__`` applies to dict + keys; requests exist (`python/cpython#63020`_, `python/cpython#117391`_, + `python/cpython#85741`_, `python/cpython#117592`_). A compatible future + extension is to consult the hooks exactly where the ``keys must be + str...`` :exc:`TypeError` is raised today (before the ``skipkeys`` + skip), so conforming keys pay nothing; the hook result would re-enter + key normalization, and container or raw results would be rejected. + Deferred from this PEP. + +* **Cycle detection through hooks.** A hook returning a fresh + equal-but-not-identical cycle each call exhausts the recursion limit + (:exc:`RecursionError`) rather than reporting ``Circular reference + detected``. Tracking hook results in the ``check_circular`` markers + would close this at some cost to the hook path; deferred pending + evidence it matters in practice. + +* **``__raw_json__`` as a pure marker.** An open alternative keeps + ``__raw_json__`` as the marker but takes the emitted fragment from + ``str(o)`` instead of the method's return value — ``RawJSON`` + defines ``__str__`` returning the fragment for exactly this reason, + and the same would hold if raw wrappers were recognized by identity + or name. Calling the ``__str__`` slot is faster than a full method + call, but the design would *require* every raw wrapper to define + both ``__raw_json__`` and ``__str__``. + + +Appendix: Related issues +======================== + +The CPython issues referenced in this PEP, in chronological order: + +* `enum.IntEnum is not compatible with JSON serialisation + `__ + (python/cpython#62464, closed) +* `json.dumps() claims numpy.ndarray and numpy.bool_ are not + serializable `__ + (python/cpython#62503, closed) +* `json.dump() ignores its 'default' option when serializing dictionary + keys `__ + (python/cpython#63020, open) +* `collections.deque should ship with a stdlib json serializer + `__ + (python/cpython#64973, open) +* `json library fails to serialize objects such as datetime + `__ + (python/cpython#65742, closed) +* `Only READ support for Decimal in json + `__ + (python/cpython#67312, open) +* `Allow namedtuple to be JSON encoded as dict + `__ + (python/cpython#67661, closed) +* `json fails to serialise numpy.int64 + `__ + (python/cpython#68501, closed) +* `Serialize array.array to JSON by default + `__ + (python/cpython#70451, open) +* `json.dumps to check for obj.__json__ before raising TypeError + `__ + (python/cpython#71549, open) +* `Make collections.deque json serializable + `__ + (python/cpython#73849, closed) +* `Encode set, frozenset, bytearray, and iterators as json arrays + `__ + (python/cpython#75338, closed) +* `json int encoding incorrect for dbus.Byte + `__ + (python/cpython#77978, closed) +* `Json.dump() bug when using generator + `__ + (python/cpython#78031, closed) +* `MappingProxy objects should JSON serialize just like a dictionary + `__ + (python/cpython#79039, open) +* `Make Custom Object Classes JSON Serializable + `__ + (python/cpython#79292, open) +* `Supporting customization of float encoding in JSON + `__ + (python/cpython#81022, open) +* `json fails to encode dictionary view types + `__ + (python/cpython#83377, closed) +* `json.JSONEncoder.default should be called for dict keys as well + `__ + (python/cpython#85741, closed) +* `Introduce new data model method __iter_items__ + `__ + (python/cpython#86931, closed) +* `There is no way to json encode object to str. + `__ + (python/cpython#86957, open) +* `Proper or custom JSON serialization of non-finite float values + `__ + (python/cpython#98306, open) +* `Json encode from __repr__, __str__ or __serialize__ when available + `__ + (python/cpython#114285, closed) +* `Allow JSONEncoder to handle passing unsupported dict keys through + .default() before throwing TypeError + `__ + (python/cpython#117391, open) +* `json's default callable/method ignores keys. + `__ + (python/cpython#117592, closed) +* `Allow the JSON encoder to optionally support decimal.Decimal objects + `__ + (python/cpython#118810, closed) +* `Allow customization of NaN and Infinity serialization in json module + `__ + (python/cpython#134717, closed) +* `Optional Decimal to JSON Number Conversion in json Module + `__ + (python/cpython#145115, closed) + + +.. _July 2010: https://mail.python.org/pipermail/python-ideas/2010-July/007811.html +.. _April 2020: https://mail.python.org/archives/list/python-ideas@python.org/thread/ISSUQVYI5OYYXKELUNCD5YCEDZ75LCEB/ +.. _discuss.python.org in 2022–2024: https://discuss.python.org/t/introduce-a-json-magic-method/21768 +.. _python-ideas in 2019: https://mail.python.org/archives/list/python-ideas@python.org/thread/WT6Z6YJDEZXKQ6OQLGAPB3OZ4OHCTPDU/ +.. _discuss.python.org in 2022: https://discuss.python.org/t/json-register/20289 +.. _TurboGears: https://turbogears.readthedocs.io/en/latest/cookbook/jsonp.html +.. _Pyramid's JSON renderer: https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/renderers.html +.. _simplejson: https://simplejson.readthedocs.io/ + +.. _python/cpython#62464: https://github.com/python/cpython/issues/62464 +.. _python/cpython#62503: https://github.com/python/cpython/issues/62503 +.. _python/cpython#63020: https://github.com/python/cpython/issues/63020 +.. _python/cpython#64973: https://github.com/python/cpython/issues/64973 +.. _python/cpython#65742: https://github.com/python/cpython/issues/65742 +.. _python/cpython#67312: https://github.com/python/cpython/issues/67312 +.. _python/cpython#67661: https://github.com/python/cpython/issues/67661 +.. _python/cpython#68501: https://github.com/python/cpython/issues/68501 +.. _python/cpython#70451: https://github.com/python/cpython/issues/70451 +.. _python/cpython#71549: https://github.com/python/cpython/issues/71549 +.. _python/cpython#73849: https://github.com/python/cpython/issues/73849 +.. _python/cpython#75338: https://github.com/python/cpython/issues/75338 +.. _python/cpython#77978: https://github.com/python/cpython/issues/77978 +.. _python/cpython#78031: https://github.com/python/cpython/issues/78031 +.. _python/cpython#79039: https://github.com/python/cpython/issues/79039 +.. _python/cpython#79292: https://github.com/python/cpython/issues/79292 +.. _python/cpython#81022: https://github.com/python/cpython/issues/81022 +.. _python/cpython#83377: https://github.com/python/cpython/issues/83377 +.. _python/cpython#85741: https://github.com/python/cpython/issues/85741 +.. _python/cpython#86931: https://github.com/python/cpython/issues/86931 +.. _python/cpython#86957: https://github.com/python/cpython/issues/86957 +.. _python/cpython#98306: https://github.com/python/cpython/issues/98306 +.. _python/cpython#114285: https://github.com/python/cpython/issues/114285 +.. _python/cpython#117391: https://github.com/python/cpython/issues/117391 +.. _python/cpython#117592: https://github.com/python/cpython/issues/117592 +.. _python/cpython#118810: https://github.com/python/cpython/issues/118810 +.. _python/cpython#134717: https://github.com/python/cpython/issues/134717 +.. _python/cpython#145115: https://github.com/python/cpython/issues/145115 + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. From 3725ee1d143c5d85e647dae8c94b1151162968ef Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jul 2026 08:50:55 +0300 Subject: [PATCH 2/5] PEP 837: Link the reference implementation PR Co-Authored-By: Claude Fable 5 --- peps/pep-0837.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/peps/pep-0837.rst b/peps/pep-0837.rst index 97a79317e67..596bfba8ed5 100644 --- a/peps/pep-0837.rst +++ b/peps/pep-0837.rst @@ -477,11 +477,12 @@ no other mechanism handled and is documented as running last. Reference Implementation ======================== -Branch ``json-customize4`` (three commits), a rebase and rework of the -author's 2017–2022 ``json-customize`` branches: the protocol and -registry with C and Python encoder support, standard library -``__json__`` methods, and tests for both encoder implementations, -including the encoding-pipeline invariant recorded as code comments. +`python/cpython#153607 `__ +(branch ``json-customize4``), a rebase and rework of the author's +2017–2022 ``json-customize`` branches: the protocol and registry with C +and Python encoder support, standard library ``__json__`` methods, and +tests for both encoder implementations, including the encoding-pipeline +invariant recorded as code comments. Rejected Ideas From 4256286ce0dbd3e91ba0a7bff400235935622c5f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jul 2026 09:18:38 +0300 Subject: [PATCH 3/5] PEP 837: Add Discussions-To and Post-History Co-Authored-By: Claude Fable 5 --- peps/pep-0837.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/peps/pep-0837.rst b/peps/pep-0837.rst index 596bfba8ed5..ea6737bd6c9 100644 --- a/peps/pep-0837.rst +++ b/peps/pep-0837.rst @@ -1,10 +1,12 @@ PEP: 837 Title: Extensible JSON serialization Author: Serhiy Storchaka +Discussions-To: https://discuss.python.org/t/pep-837-extensible-json-serialization/108124 Status: Draft Type: Standards Track Created: 11-Jul-2026 Python-Version: 3.16 +Post-History: `12-Jul-2026 `__ Abstract From c3f19bdb0aed6ad86e2f05c0e67f6463fe04dc6e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jul 2026 12:47:28 +0300 Subject: [PATCH 4/5] PEP 837: Cite simplejson issue 52 for the for_json naming rationale Co-Authored-By: Claude Fable 5 --- peps/pep-0837.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/peps/pep-0837.rst b/peps/pep-0837.rst index ea6737bd6c9..af70e8c2db2 100644 --- a/peps/pep-0837.rst +++ b/peps/pep-0837.rst @@ -108,7 +108,11 @@ ordering as this PEP. `Pyramid's JSON renderer`_ calls ``__json__(request)``. Applications and libraries such as conda, Checkmk and TatSu define ``__json__`` today. `simplejson`_ added an opt-in ``for_json()`` method instead — choosing that name precisely because dunder names are -reserved for the language. Only the standard library can define +reserved for the language (`simplejson issue #52`_). In that issue's +discussion, a GitHub code search already found more ``__json__`` +definitions in the wild than ``for_json`` ones by 2016, and formalizing +the protocol in a PEP was requested so that third-party libraries can +rely on the same signature. Only the standard library can define ``__json__``; this PEP does, ending the fragmentation. The type-support requests could not simply be granted one by one. For @@ -685,6 +689,7 @@ The CPython issues referenced in this PEP, in chronological order: .. _TurboGears: https://turbogears.readthedocs.io/en/latest/cookbook/jsonp.html .. _Pyramid's JSON renderer: https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/renderers.html .. _simplejson: https://simplejson.readthedocs.io/ +.. _simplejson issue #52: https://github.com/simplejson/simplejson/issues/52 .. _python/cpython#62464: https://github.com/python/cpython/issues/62464 .. _python/cpython#62503: https://github.com/python/cpython/issues/62503 From d74583534f1ec883b3960691081b06995cae68da Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jul 2026 12:59:42 +0300 Subject: [PATCH 5/5] PEP 837: Use the cpython-issue role for issue references Also fix the markup of the '__raw_json__ as a pure marker' bullet (inline markup cannot be nested) and escape numpy.bool_ in the appendix. Co-Authored-By: Claude Fable 5 --- peps/pep-0837.rst | 223 +++++++++++++++++----------------------------- 1 file changed, 82 insertions(+), 141 deletions(-) diff --git a/peps/pep-0837.rst b/peps/pep-0837.rst index af70e8c2db2..64028d54804 100644 --- a/peps/pep-0837.rst +++ b/peps/pep-0837.rst @@ -63,8 +63,8 @@ the knowledge to the *call* rather than to the *type*: * **Third-party types cannot opt in.** A library defining a new type has no way to make it JSON-serializable for its users; every user must learn and repeat the incantation. Requests to fix this for NumPy - scalars and arrays (`python/cpython#68501`_, `python/cpython#62503`_) and - for D-Bus integer types (`python/cpython#77978`_) were closed as + scalars and arrays (:cpython-issue:`68501`, :cpython-issue:`62503`) and + for D-Bus integer types (:cpython-issue:`77978`) were closed as out of scope for the standard library — correctly, but leaving the underlying need unmet. @@ -73,25 +73,25 @@ the knowledge to the *call* rather than to the *type*: *number* that ``float`` cannot represent exactly. :func:`json.loads` can read decimal numbers losslessly via ``parse_float=decimal.Decimal``, but the result cannot be written - back (`python/cpython#67312`_). The same limitation blocks control over float - formatting (`python/cpython#81022`_) and non-finite float representation - (`python/cpython#98306`_, `python/cpython#134717`_), and the general request - for emitting pre-encoded JSON (`python/cpython#86957`_). + back (:cpython-issue:`67312`). The same limitation blocks control over float + formatting (:cpython-issue:`81022`) and non-finite float representation + (:cpython-issue:`98306`, :cpython-issue:`134717`), and the general request + for emitting pre-encoded JSON (:cpython-issue:`86957`). The demand is long-standing and broad. Beyond the general proposals for a serialization hook checked before raising :exc:`TypeError` -(`python/cpython#71549`_ — open since 2016, `python/cpython#79292`_, -`python/cpython#114285`_, `python/cpython#86931`_), the tracker accumulated +(:cpython-issue:`71549` — open since 2016, :cpython-issue:`79292`, +:cpython-issue:`114285`, :cpython-issue:`86931`), the tracker accumulated requests for out-of-the-box support of concrete standard library types: -:class:`decimal.Decimal` (`python/cpython#67312`_, `python/cpython#118810`_, -`python/cpython#145115`_), :class:`array.array` (`python/cpython#70451`_), -:class:`collections.deque` (`python/cpython#64973`_, `python/cpython#73849`_), -:class:`types.MappingProxyType` (`python/cpython#79039`_), -:func:`collections.namedtuple` as an object (`python/cpython#67661`_), and -:class:`datetime.datetime` (`python/cpython#65742`_); and for whole +:class:`decimal.Decimal` (:cpython-issue:`67312`, :cpython-issue:`118810`, +:cpython-issue:`145115`), :class:`array.array` (:cpython-issue:`70451`), +:class:`collections.deque` (:cpython-issue:`64973`, :cpython-issue:`73849`), +:class:`types.MappingProxyType` (:cpython-issue:`79039`), +:func:`collections.namedtuple` as an object (:cpython-issue:`67661`), and +:class:`datetime.datetime` (:cpython-issue:`65742`); and for whole categories: sets, frozensets, bytearrays and iterators -(`python/cpython#75338`_), generators (`python/cpython#78031`_), and -dictionary views (`python/cpython#83377`_). +(:cpython-issue:`75338`), generators (:cpython-issue:`78031`), and +dictionary views (:cpython-issue:`83377`). The idea has also been proposed independently for sixteen years, each time reinventing part of this design: a ``__json__()`` method on @@ -268,7 +268,7 @@ library, and the design treats its branch order as an invariant: fallbacks, solely so that subclasses of the basic types can customize their serialization at all (an ``int`` subclass would otherwise be consumed by the ``isinstance(o, int)`` check, the very - problem history records for ``IntEnum``: `python/cpython#62464`_). + problem history records for ``IntEnum``: :cpython-issue:`62464`). 3. The ``isinstance`` fallbacks then catch non-customizing subclasses, preserving today's behavior for them exactly (``IntEnum`` still @@ -291,8 +291,8 @@ The criterion: **a standard library type gets a default ``__json__`` only when its JSON form is unambiguous.** Transparent containers qualify — ``deque``, ``mappingproxy``, ``ChainMap``, ``UserDict``, ``UserList``, ``UserString`` are semantically just arrays and objects -(resolving `python/cpython#64973`_, `python/cpython#73849`_ and -`python/cpython#79039`_ directly). ``Decimal``, ``namedtuple``, +(resolving :cpython-issue:`64973`, :cpython-issue:`73849` and +:cpython-issue:`79039` directly). ``Decimal``, ``namedtuple``, ``array.array`` and ``datetime`` do not qualify, for the ambiguity reasons given in the Motivation; for them, this PEP's answer is a one-line registration by the application (see `How to Teach This`_). @@ -483,8 +483,7 @@ no other mechanism handled and is documented as running last. Reference Implementation ======================== -`python/cpython#153607 `__ -(branch ``json-customize4``), a rebase and rework of the author's +:cpython-pr:`153607` (branch ``json-customize4``), a rebase and rework of the author's 2017–2022 ``json-customize`` branches: the protocol and registry with C and Python encoder support, standard library ``__json__`` methods, and tests for both encoder implementations, including the encoding-pipeline @@ -512,8 +511,8 @@ Default ``__json__`` for Decimal, namedtuple, array.array, datetime not for a default. Serializing all iterables and mappings without opt-in - Requested in `python/cpython#75338`_ and implied by - `python/cpython#78031`_ and `python/cpython#83377`_, but a silent behavior + Requested in :cpython-issue:`75338` and implied by + :cpython-issue:`78031` and :cpython-issue:`83377`, but a silent behavior change: every object with ``__iter__`` (sets, generators, file objects) would begin serializing as an array instead of reaching ``default=`` or raising. Duck typing therefore applies only to hook @@ -544,7 +543,7 @@ Consulting ``__raw_json__`` directly on all objects the raw object pay one method call instead. Reusing ``__repr__``/``__str__`` or a generic ``__serialize__`` - Proposed in `python/cpython#114285`_. These methods cannot serve as + Proposed in :cpython-issue:`114285`. These methods cannot serve as the *marker* for raw output: nearly every type defines them (and their results are usually not valid JSON), so the encoder could not tell raw-capable objects apart; a format-agnostic ``__serialize__`` @@ -564,8 +563,8 @@ Open Issues =========== * **Dict keys.** Neither the registry nor ``__json__`` applies to dict - keys; requests exist (`python/cpython#63020`_, `python/cpython#117391`_, - `python/cpython#85741`_, `python/cpython#117592`_). A compatible future + keys; requests exist (:cpython-issue:`63020`, :cpython-issue:`117391`, + :cpython-issue:`85741`, :cpython-issue:`117592`). A compatible future extension is to consult the hooks exactly where the ``keys must be str...`` :exc:`TypeError` is raised today (before the ``skipkeys`` skip), so conforming keys pay nothing; the hook result would re-enter @@ -579,7 +578,7 @@ Open Issues would close this at some cost to the hook path; deferred pending evidence it matters in practice. -* **``__raw_json__`` as a pure marker.** An open alternative keeps +* **__raw_json__ as a pure marker.** An open alternative keeps ``__raw_json__`` as the marker but takes the emitted fragment from ``str(o)`` instead of the method's return value — ``RawJSON`` defines ``__str__`` returning the fragment for exactly this reason, @@ -594,91 +593,62 @@ Appendix: Related issues The CPython issues referenced in this PEP, in chronological order: -* `enum.IntEnum is not compatible with JSON serialisation - `__ - (python/cpython#62464, closed) -* `json.dumps() claims numpy.ndarray and numpy.bool_ are not - serializable `__ - (python/cpython#62503, closed) -* `json.dump() ignores its 'default' option when serializing dictionary - keys `__ - (python/cpython#63020, open) -* `collections.deque should ship with a stdlib json serializer - `__ - (python/cpython#64973, open) -* `json library fails to serialize objects such as datetime - `__ - (python/cpython#65742, closed) -* `Only READ support for Decimal in json - `__ - (python/cpython#67312, open) -* `Allow namedtuple to be JSON encoded as dict - `__ - (python/cpython#67661, closed) -* `json fails to serialise numpy.int64 - `__ - (python/cpython#68501, closed) -* `Serialize array.array to JSON by default - `__ - (python/cpython#70451, open) -* `json.dumps to check for obj.__json__ before raising TypeError - `__ - (python/cpython#71549, open) -* `Make collections.deque json serializable - `__ - (python/cpython#73849, closed) -* `Encode set, frozenset, bytearray, and iterators as json arrays - `__ - (python/cpython#75338, closed) -* `json int encoding incorrect for dbus.Byte - `__ - (python/cpython#77978, closed) -* `Json.dump() bug when using generator - `__ - (python/cpython#78031, closed) -* `MappingProxy objects should JSON serialize just like a dictionary - `__ - (python/cpython#79039, open) -* `Make Custom Object Classes JSON Serializable - `__ - (python/cpython#79292, open) -* `Supporting customization of float encoding in JSON - `__ - (python/cpython#81022, open) -* `json fails to encode dictionary view types - `__ - (python/cpython#83377, closed) -* `json.JSONEncoder.default should be called for dict keys as well - `__ - (python/cpython#85741, closed) -* `Introduce new data model method __iter_items__ - `__ - (python/cpython#86931, closed) -* `There is no way to json encode object to str. - `__ - (python/cpython#86957, open) -* `Proper or custom JSON serialization of non-finite float values - `__ - (python/cpython#98306, open) -* `Json encode from __repr__, __str__ or __serialize__ when available - `__ - (python/cpython#114285, closed) -* `Allow JSONEncoder to handle passing unsupported dict keys through - .default() before throwing TypeError - `__ - (python/cpython#117391, open) -* `json's default callable/method ignores keys. - `__ - (python/cpython#117592, closed) -* `Allow the JSON encoder to optionally support decimal.Decimal objects - `__ - (python/cpython#118810, closed) -* `Allow customization of NaN and Infinity serialization in json module - `__ - (python/cpython#134717, closed) -* `Optional Decimal to JSON Number Conversion in json Module - `__ - (python/cpython#145115, closed) +* enum.IntEnum is not compatible with JSON serialisation + (:cpython-issue:`62464`, closed) +* json.dumps() claims numpy.ndarray and numpy.bool\_ are not serializable + (:cpython-issue:`62503`, closed) +* json.dump() ignores its 'default' option when serializing dictionary keys + (:cpython-issue:`63020`, open) +* collections.deque should ship with a stdlib json serializer + (:cpython-issue:`64973`, open) +* json library fails to serialize objects such as datetime + (:cpython-issue:`65742`, closed) +* Only READ support for Decimal in json + (:cpython-issue:`67312`, open) +* Allow namedtuple to be JSON encoded as dict + (:cpython-issue:`67661`, closed) +* json fails to serialise numpy.int64 + (:cpython-issue:`68501`, closed) +* Serialize array.array to JSON by default + (:cpython-issue:`70451`, open) +* json.dumps to check for obj.__json__ before raising TypeError + (:cpython-issue:`71549`, open) +* Make collections.deque json serializable + (:cpython-issue:`73849`, closed) +* Encode set, frozenset, bytearray, and iterators as json arrays + (:cpython-issue:`75338`, closed) +* json int encoding incorrect for dbus.Byte + (:cpython-issue:`77978`, closed) +* Json.dump() bug when using generator + (:cpython-issue:`78031`, closed) +* MappingProxy objects should JSON serialize just like a dictionary + (:cpython-issue:`79039`, open) +* Make Custom Object Classes JSON Serializable + (:cpython-issue:`79292`, open) +* Supporting customization of float encoding in JSON + (:cpython-issue:`81022`, open) +* json fails to encode dictionary view types + (:cpython-issue:`83377`, closed) +* json.JSONEncoder.default should be called for dict keys as well + (:cpython-issue:`85741`, closed) +* Introduce new data model method __iter_items__ + (:cpython-issue:`86931`, closed) +* There is no way to json encode object to str. + (:cpython-issue:`86957`, open) +* Proper or custom JSON serialization of non-finite float values + (:cpython-issue:`98306`, open) +* Json encode from __repr__, __str__ or __serialize__ when available + (:cpython-issue:`114285`, closed) +* Allow JSONEncoder to handle passing unsupported dict keys through .default() before throwing TypeError + (:cpython-issue:`117391`, open) +* json's default callable/method ignores keys. + (:cpython-issue:`117592`, closed) +* Allow the JSON encoder to optionally support decimal.Decimal objects + (:cpython-issue:`118810`, closed) +* Allow customization of NaN and Infinity serialization in json module + (:cpython-issue:`134717`, closed) +* Optional Decimal to JSON Number Conversion in json Module + (:cpython-issue:`145115`, closed) .. _July 2010: https://mail.python.org/pipermail/python-ideas/2010-July/007811.html @@ -691,35 +661,6 @@ The CPython issues referenced in this PEP, in chronological order: .. _simplejson: https://simplejson.readthedocs.io/ .. _simplejson issue #52: https://github.com/simplejson/simplejson/issues/52 -.. _python/cpython#62464: https://github.com/python/cpython/issues/62464 -.. _python/cpython#62503: https://github.com/python/cpython/issues/62503 -.. _python/cpython#63020: https://github.com/python/cpython/issues/63020 -.. _python/cpython#64973: https://github.com/python/cpython/issues/64973 -.. _python/cpython#65742: https://github.com/python/cpython/issues/65742 -.. _python/cpython#67312: https://github.com/python/cpython/issues/67312 -.. _python/cpython#67661: https://github.com/python/cpython/issues/67661 -.. _python/cpython#68501: https://github.com/python/cpython/issues/68501 -.. _python/cpython#70451: https://github.com/python/cpython/issues/70451 -.. _python/cpython#71549: https://github.com/python/cpython/issues/71549 -.. _python/cpython#73849: https://github.com/python/cpython/issues/73849 -.. _python/cpython#75338: https://github.com/python/cpython/issues/75338 -.. _python/cpython#77978: https://github.com/python/cpython/issues/77978 -.. _python/cpython#78031: https://github.com/python/cpython/issues/78031 -.. _python/cpython#79039: https://github.com/python/cpython/issues/79039 -.. _python/cpython#79292: https://github.com/python/cpython/issues/79292 -.. _python/cpython#81022: https://github.com/python/cpython/issues/81022 -.. _python/cpython#83377: https://github.com/python/cpython/issues/83377 -.. _python/cpython#85741: https://github.com/python/cpython/issues/85741 -.. _python/cpython#86931: https://github.com/python/cpython/issues/86931 -.. _python/cpython#86957: https://github.com/python/cpython/issues/86957 -.. _python/cpython#98306: https://github.com/python/cpython/issues/98306 -.. _python/cpython#114285: https://github.com/python/cpython/issues/114285 -.. _python/cpython#117391: https://github.com/python/cpython/issues/117391 -.. _python/cpython#117592: https://github.com/python/cpython/issues/117592 -.. _python/cpython#118810: https://github.com/python/cpython/issues/118810 -.. _python/cpython#134717: https://github.com/python/cpython/issues/134717 -.. _python/cpython#145115: https://github.com/python/cpython/issues/145115 - Copyright =========