Repository navigation
Add PyTuple_FromPair #145247
Description
Activity
- addedtype-featureA feature request or enhancementA feature request or enhancementinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)3.15bugs and security fixesbugs and security fixes
on Feb 26, 2026 Minor correction:
Some of the above PR suggest replacingPyTuple_Pack.
In terms of clarity or safety there is no real improvement in usingPyTuple_FromPair, but there is a significant performance advantage;PyTuple_Packis quite cumbersomeIf we decide to add this, I would like to work on it, if no one minds.
If we decide to add this, I would like to work on it, if no one minds.
Please, go ahead.
I think the only real debate is which, if any, of these functions should go in the public API.
If you make the functions private initially, then there should be no objectionIf you do want to add
FromSingleandFromTriplet, could you do them in separate PRs later to avoid making the initial PR too big or contentious.Reacted by Sergey Miryanov and Kirill PodoprigoraI think the only real debate is which, if any, of these functions should go in the public API.
The C API Working Group voted against adding a public C API to create 1-tuple or 2-tuple. So I suggest adding an internal C API instead.
Results from microbenchmarks, run under Ubuntu 24.04, CPU i5-11600K @ 3.90GHz.
When storing List to the tuple - List is gc-trackable, so tuple will be tracked too (microbenchmark):
bench_tuple_new_pair: Mean +- std dev: 39.7 ns +- 2.6 ns bench_tuple_pack_pair: Mean +- std dev: 40.8 ns +- 1.5 ns bench_tuple_from_array_pair: Mean +- std dev: 36.4 ns +- 2.6 ns bench_tuple_from_array_pair_steal: Mean +- std dev: 35.8 ns +- 2.0 ns bench_tuple_from_pair: Mean +- std dev: 34.7 ns +- 2.1 ns bench_tuple_from_pair_steal: Mean +- std dev: 35.7 ns +- 1.9 nsWhen storing Long to the tuple - Long is not gc-trackable, so tuple will not be tracked (microbenchmark):
bench_tuple_new_pair: Mean +- std dev: 22.9 ns +- 0.2 ns bench_tuple_pack_pair: Mean +- std dev: 20.9 ns +- 0.3 ns bench_tuple_from_array_pair: Mean +- std dev: 18.8 ns +- 0.1 ns bench_tuple_from_array_pair_steal: Mean +- std dev: 19.1 ns +- 0.2 ns bench_tuple_from_pair: Mean +- std dev: 18.4 ns +- 0.1 ns bench_tuple_from_pair_steal: Mean +- std dev: 16.1 ns +- 0.1 nsScript for run microbenchmarks
import _testinternalcapi import pyperf def add_cmdline_args(cmd, args): cmd.append(args.what) def main(): runner = pyperf.Runner(add_cmdline_args=add_cmdline_args) runner.argparser.add_argument('what', choices=['all', ]) args = runner.parse_args() if args.what.lower() in ('all', ): runner.bench_time_func('bench_tuple_new_pair', _testinternalcapi.bench_tuple_new_pair) runner.bench_time_func('bench_tuple_pack_pair', _testinternalcapi.bench_tuple_pack_pair) runner.bench_time_func('bench_tuple_from_array_pair', _testinternalcapi.bench_tuple_from_array_pair) runner.bench_time_func('bench_tuple_from_array_pair_steal', _testinternalcapi.bench_tuple_from_array_pair_steal) runner.bench_time_func('bench_tuple_from_pair', _testinternalcapi.bench_tuple_from_pair) runner.bench_time_func('bench_tuple_from_pair_steal', _testinternalcapi.bench_tuple_from_pair_steal) if __name__ == '__main__': main()I have a branch where I replaced old construction of 2-tuples with new API. It touches a lot of modules. Should I create a separate PR for each module?
- added a commit that references this issue
on Mar 10, 2026 I merged #145325 which implements _PyTuple_FromPair() and _PyTuple_FromPairSteal().
I have a branch where I replaced old construction of 2-tuples with new API. It touches a lot of modules. Should I create a separate PR for each module?
I suggest creating a first PR which modify a maximum of 10 files. If this change is merged, move on to the next batch.
Reacted by Sergey MiryanovI suggest creating a first PR which modify a maximum of 10 files. If this change is merged, move on to the next batch.
Thanks!
5 remaining items
@sergey-miryanov: You can CC-me if you write more changes to use these new functions.
@vstinner Ok, thanks! Will prepare PRs this week.
Reacted by Victor Stinner- added 6 commits that reference this issue
on Apr 25, 2026 Triage: Is work still on-going or can this be closed?
I plan to review the codebase for additional use cases for
_PyTuple_FromPair, and evaluate whether adding_PyTuple_FromSingleand_PyTuple_FromTripletvariants makes sense.Reacted by Stan Ulbrych
Feature or enhancement
Proposal:
Rationale
We have had quite a few PRs lately suggesting replacing
PyTuple_New(which requires manually setting the items) withPyTuple_FromArray.Many of them have been rejected because of (maybe excessive) requirements for the authors to do benchmarking, even though the code is IMO a clear improvement. Almost all of these have been for pairs.
I propose adding
PyTuple_FromPairto provide an idiomatic, fast and safe way to create 2-tuples.We should also add a "steal" variant as well to simplify replacing
PyTuple_New() ... PyTuple_SET_ITEM.Here's all the relevant PRs I could easily find:
#144529
#144531
#144532
#144760
#144771
#144772
#144773
#144829
Prior abandoned issue:
#140052 which also proposes adding
PyTuple_FromSingleand which links to capi-workgroup/decisions#84.capi-workgroup/decisions#84 rejects the idea without any clear rationale.
Clarity
compare with
Performance
Compared to
PyTuple_NewPyTuple_FromPaircan allocate directly from the free list without additional checks. It does not need to check for weird sizes, or special case size zero. It does not need to NULL out the items before they are set. It can determine whether the tuple needs to be tracked or not at construction without the overhead of tracking it, then later untracking it.Compared to
PyTuple_FromArrayThe advantage is less than for
PyTuple_New, but still significant, as it reduces memory traffic by not storing the items in an array, then reading them out again.Safety
This has the same safety advantages as
PyTuple_FromArray, as it never produces an incomplete tuple. Since much of the code it will replace is usingPyTuple_New, this is a worthwhile improvementImplementation
We should make this a private API to start with. Once it proves it use, then we can make it public.
Linked PRs