cpython

What does it do?

The repository is for Python, described as “The Python programming language” and titled as Python version 3.16.0 alpha 1+dev. [1a][1b] It provides source code and build instructions for producing a Python installation, which the build installs as python3. [2a][2b] A documented use case is testing the built Python by running make test in the top-level directory. [3a][3b]

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

78posts · 30d
Nov 31 post
  1. Oh fun, the reason I can't compile python 3.14 is because ... the time library cannot handle a mid-year tzdata change. cpython#161230 America/Vancouver broke its brain.

  2. Bluesky · · @mo.rijndael.cc

    не каждый день в процессе отладки маааааленького скрипта-краулера натыкаешься на баги синхронизации в рантайме языка... :neocat_shocked_googly_woozy: cpython#156321 клаулер плевался в лог огромной йобой на ошибки, которые должны были быть пойманы и отработаны в […]

    1 like
  3. Naprawa: w pliku ~/.claude/settings.json, w sekcji "env", dopisz "PYTHON_BASIC_REPL": "1" Python bez kodu kończy się wtedy od razu. cpython#159323 claude-code#105575 (3/3)

    1 like · 47 views
  4. Reddit · · r/Python · u/kirara0048

    PEP 851 – Add list.discard() PEP 851 – Add list.discard() peps.python.org Discussions-To: Discourse thread Abstract Add list.discard(x), which removes the first item equal to x if one is present and does nothing otherwise, returning whether an item was removed. It is the non-raising counterpart to list.remove(), which currently has none. The name is taken from set.discard(), which already means “remove if present” in Python. Motivation Removing a value that may not be present is usually written as: if x in lst: lst.remove(x) or: try: lst.remove(x) except ValueError: pass The first form is clear but scans the list twice on a hit, and if the list is shared between threads another thread can remove the item between the two calls, turning a no-op into a ValueError. The second avoids both problems but obscures the intent. It is four lines for one operation, pays for raising and catching an exception on every miss, and except ValueError is broad enough to hide unrelated errors from a user-defined __eq__(). Both are common. A search of the 5,000 most downloaded PyPI projects finds 1,062 matches for the first form across 411 projects and 331 for the second across 164 projects. Some of these are on other container types; in the standard library, excluding tests, two of the 29 matches are on deques and the remaining seven and 20 are on lists, for example in logging, warnings, tkinter, runpy and asyncio. Each becomes a single call that states the intent directly: lst.discard(x) The cost of providing this is small, as discard() reuses the existing search and removal implementation of remove().

    170 points · 45 comments
  5. DEV · · @alanwest

    Python's JIT Is Being Removed After a 7-8% Gain. Here's What Makes Python Faster CPython's experimental JIT is being removed. It shipped in three releases (3.13, 3.14, 3.15), got a rewritten tracing frontend, register allocation and an LLVM 21 build dependency, and the official number on x86-64 Linux is a 7-8% geometric mean speedup. On October 19, 2026 the authors withdrew PEP 836, and the JIT code is expected to leave main early in the 3.16 cycle. My take: good. The JIT was never going to fix slow Python, and the time teams spent waiting for it would have been better spent on things that already work. If you were counting on it, here's how it got pulled, what it means for PYTHON_JIT and your builds, and what I'd use instead. How the CPython JIT ended up on the way out The short version, from LWN's write-up: - Brandt Bucher merged the copy-and-patch JIT for 3.13 in early 2024 (PR #113465) without a PEP. PEP 744 came afterwards, and it was Informational, not Standards Track. - On June 5, 2026 the Steering Council froze new JIT work on main. Bug and security fixes were still allowed. The council gave six months for a Standards Track PEP to be submitted and resolved. If none was accepted, "the JIT code must be removed from the main branch." - On July 3 Bucher, Savannah Ostrowski and Ken Jin posted PEP 836, "JIT Go Brrr". - On September 21 the council replied with a ten-point list covering things like memory impact, platform parity, user impact, security ("an underserved area in the PEP"), and a compatibility review the PEP scheduled for the first 3.17 release candidate, around August 2028. The council called that "way, way too late." - On October 19 Ostrowski withdrew the PEP. Per LWN: "we don't feel we have the personal bandwidth to carry the current version forward." So on paper this was a process decision. The JIT was paused, the authors were asked for a plan they could commit to, and they decided they couldn't carry it. The performance numbers are why nobody fought very hard to keep it. The JIT's numbers never matched the hype Here are the numbers the JIT's own docs and PEP published: | Release | Official JIT claim | Source | |---|---|---| | 3.14 | "can range from 10% slower to 20% faster, depending on workload" | What's New 3.14 | | 3.15 | 7-8% geomean on x86-64 Linux; 11-12% on AArch64 macOS vs the tail-calling interpreter | What's New 3.15 | | 3.15, per benchmark | "roughly 15% slowdown to over 100% speedup" | same | | PEP 836 runs, June 2026 | 1.047x (Windows, Ryzen 5 3600X) to 1.126x (M3 Pro macOS) | PEP 836 | That 3.15 per-benchmark range means some benchmarks run 15% slower with the JIT on. If you run a web service, the geomean doesn't tell you much; you get whatever your own code paths get. PEP 836 set its own target, and it asked for at least a 20% geomean improvement on pyperformance for JIT plus free-threading over the plain free-threaded interpreter, by the first 3.17 beta. The 3.15 number on the default x86-64 build was roughly a third of that. Different baseline, I know, but that's a big gap. For comparison, the 3.14 tail-calling interpreter showed 3-5% on pyperformance in preliminary benchmarks, just from changing how the interpreter dispatches opcodes. It needs a recent compiler (Clang 19 at the time), but no new compiler tier inside CPython and no new block of runtime-generated machine code to secure. The people who built it did serious work in 3.15: a trace-recording frontend, reference-count elimination, GDB unwinding through JIT frames. The problem is Python itself. As matklad pointed out on Lobsters, in Python "a lot of code that's being executed is the C code of the interpreter itself", and a bytecode JIT doesn't speed that up. It doesn't touch the C extensions most real programs lean on. Antonio Cuni, who has years of PyPy behind him, was blunter in the same thread: writing a Python implementation that is "real fast" and backwards compatible is "de facto impossible". He also says PyPy proves Python itself is optimizable, so his complaint is about compatibility, not the language. What happens to PYTHON_JIT and your builds - On 3.13, 3.14 and 3.15 nothing changes. LWN reports the council said it was OK with the status quo for 3.15. The official macOS and Windows binaries ship with the JIT built but off, and PYTHON_JIT=1 turns it on. - If you build from source, --enable-experimental-jit (and =yes-off for "built but disabled") still works on those branches. You still need LLVM at build time; for 3.15 that's LLVM 21. - From 3.16 on, the code is expected to be removed from main early in the cycle, per LWN. Plan for the configure flag and the env var to do nothing, or to disappear. - The code itself isn't going away entirely. Ostrowski said the reference implementation will be published and linked from the withdrawn PEP. Brett Cannon said work stops in the main repo; whether it continues elsewhere is up to the community. Check whether a given interpreter has it: import sys jit = getattr(sys, "_jit", None) if jit is None: print("no sys._jit here (Python < 3.14)") else: print("available:", jit.is_available(), "enabled:", jit.is_enabled()) sys._jit was added in 3.14 and has a leading underscore, so it won't exist on 3.13 and probably won't survive 3.16. Don't build anything on it. What actually makes Python faster in 2026 None of this is new, and all of it is less exciting than "Python gets a JIT". Profile, then fix the algorithm 3.15 ships the Tachyon sampling profiler (PEP 799) as profiling.sampling, and the docs claim sampling rates "up to 1,000,000 Hz" with virtually zero overhead. On older versions, use py-spy. In my experience the hot spot is usually a quadratic loop or an N+1 query, and no JIT would have saved you from either. Compile the hot module with mypyc, Cython or Numba In the HN thread, one commenter reported a fib benchmark dropping from 1.45s to 0.064s with mypyc. If your code already has type hints, all mypyc costs you is a build step. For numeric loops, Numba is still the practical answer. Move the hot core to Rust with PyO3 PyO3 and maturin made this boring, and boring is good. pydantic-core, Polars and orjson all keep Python at the surface and do the heavy lifting in Rust. Free-threading for CPU-bound parallel work 3.14 made the free-threaded build officially supported, with a single-threaded penalty of roughly 5-10%. For CPU-bound work on threads, that beats the JIT's single-digit gain because it scales with cores. (In 3.14 you couldn't have both anyway: free-threaded builds don't support the JIT.) 3.15 adds an abi3t stable ABI for free-threaded extensions, which should shorten the "is my C dependency safe" wait. Try it: uv python install 3.14t uv run --python 3.14t python -c "import sys; print(sys._is_gil_enabled())" False means the GIL is really off. If an extension that doesn't declare free-threading support gets imported, CPython turns the GIL back on and warns you. Startup time and tooling 3.15's explicit lazy import (PEP 810) and -X lazy_imports=all matter more for CLIs than any JIT ever would. On the tooling side, uv's README claims it is "10-100x faster" than pip, which is the kind of speedup your CI pipeline will actually notice. Bytecode dispatch was never what made it slow. PyPy or GraalPy, if you can Both show bigger speedups are possible, but HN commenters point out PyPy's newest supported version is 3.12 and loading arbitrary C extensions is still the weak spot. Checklist if you were counting on the JIT 1. Search CI configs, Dockerfiles and env files for PYTHON_JIT and --enable-experimental-jit. Keep them on 3.13-3.15 if you measured a win, and mark them for removal on 3.16. 2. If you build CPython with the JIT, drop the LLVM build dependency when you move to 3.16. 3. Rerun your own benchmarks with PYTHON_JIT=0. If the difference is under your normal noise, delete the setting today. 4. Profile production with py-spy or Tachyon and find your top three hot functions. 5. For each one, decide: algorithm fix, mypyc/Cython/Numba, or a Rust extension through PyO3. 6. If you have CPU-bound threading work, test the 3.14t build and check your C extensions for free-threading support. 7. Remove any sys._jit calls from code that has to run on 3.16. Was the JIT a waste of time? I don't think the code was wasted. The register allocation and unwinding work taught the core devs something, and the reference implementation will stay public. Where it did waste time was for teams who treated "the JIT is coming" as a reason not to profile, not to move hot code out of Python, or not to try free-threading. Someone on Lobsters called the council's process "death by bureaucracy". Someone on HN called it "a pretty stellar example of non-bureaucratic governance". I'm with the second one. Pablo Galindo Salgado, who posted the announcement for the council, followed up that the six months was "a first goal to anchor, not a bureaucratic deadline we will hold", and that an extension could be requested. Writing down a maintenance plan is not a big ask for a whole new compiler tier. So where do you stand? Should the core devs have kept going until they hit that 20% bar, or was a JIT always a dead end for CPython? And what actually made your Python faster: an algorithm fix, Rust, free-threading, or leaving Python? Sources - LWN: Python's experimental JIT to be removed - Steering Council announcement, June 2026 - PEP 836 withdrawal post - PEP 836 and PEP 744 - What's New in Python 3.14 - What's New in Python 3.15 - uv README - Discussion: Hacker News, Lobsters

  6. DEV · · @vladut02

    A hash map is just an array that computes its own index --- title: A hash map is just an array that computes its own index published: true description: How a hash map turns a key into a position, what really happens on a collision, and the two things that quietly turn O(1) into a scan. tags: computerscience, python, java, programming cover_image: canonical_url: series: Vlad's Stack --- A map with a million keys answers a lookup just as fast as a map with ten. No loop. No searching. Your program jumps straight to the answer. That's not magic, and it isn't a tree. It's one very old trick, plus two failure modes that most people never hear about. > Prefer to watch? Full walkthrough with the bucket animation: > > {% youtube youtube.com %} Arrays are the fast part Start with the one thing computers are genuinely fast at: an array. Ask for item number five and nothing is searched. The machine multiplies the index by the element size, adds it to the start address, and lands on the exact spot in memory. Item five and item five million cost the same. The catch is that arrays only take positions. You don't think in positions — you want the value stored under "email", or under a user ID, or under a whole string of text. So the real question is: how do you turn a key like "email" into a position? The hash function A hash function takes anything — a word, a sentence, an object — and gives back a number. Two properties matter: - Deterministic. Same key in, same number out, every time. - Well spread. Change one letter and the result isn't close to the old one, it's unrecognizable. A hash that put similar keys near each other would pile them up in the same place. That number is usually huge, much bigger than your array. So you do the simplest possible thing: take the remainder. hash("email") = 3,742,909 3,742,909 % 8 = 5 Now you have a position. Each slot of that array is called a bucket. Look at what just happened. You didn't search for the key — you computed where it lives. Writing is hash, remainder, store. Reading is hash, remainder, load. The whole idea, in code const buckets = new Array(8) const slot = (key) => hash(key) % buckets.length // "email" → 5 buckets[slot("email")] = "vlad@stack.dev" // write buckets[slot("email")] // read There's a problem hiding in that one line. The hash space is huge, the array is small. So sooner or later, two different keys produce the same slot. Collisions are normal That's a collision, and it isn't an edge case — with 8 slots it happens almost immediately. So a bucket doesn't hold a bare value. It holds pairs: the key and the value. On a lookup you jump to the bucket, then compare keys inside it. Usually there's one pair. Sometimes two. That's also why a map has to store your key at all — otherwise it could never tell two collided keys apart. There are two ways to handle the collision, and languages pick sides: - Chaining — keep a small list inside the bucket and walk it. - Open addressing — no list; if the slot is taken, step to the next one until you find a free seat. CPython's dict works this way (see the comment at the top of Objects/dictobject.c — an implementation detail, not a language guarantee). Either way the shape is the same: land close to the answer, then take a step or two. So what does "constant time" actually mean? One hash. One jump. A tiny compare. It isn't zero work — it just doesn't get worse as the map grows. The Java docs put a condition on it: constant-time performance for get and put, assuming the hash function disperses the elements properly among the buckets. Hold on to that "assuming". It's where both failure modes live. Load factor: the resize you don't see Keep adding keys to the same small array and the buckets fill up. Those little lists get long, and comparing one by one stops being cheap. So a map watches how full it is — that ratio is the load factor. In Java, a HashMap starts with 16 buckets and a load factor of 0.75. Cross that line and the table is rebuilt with roughly twice as many buckets, and every existing key is re-slotted. Which means one unlucky insert does the work of the entire map. The average stays fast. That one call doesn't. If you know you're about to load a million entries, size the map up front. When O(1) is a lie Now the interesting failure. Imagine every key you insert lands in the same bucket. One long list; every lookup walks it. Your constant-time map is now a linear scan. That can happen by accident with a bad hash — and it can be done to you on purpose. An attacker who knows your hash function can send keys engineered to collide, turning cheap requests into expensive ones. Python's answer is to salt the hash of str and bytes with a random value at startup, explicitly as protection against that denial of service. The key that disappears This one is your own fault, and it's a classic. You store a value under a key. Then you mutate that key object. Its hash changes with it. The map now computes a different slot, looks there, and finds nothing — while your entry sits in the old bucket, unreachable. Use keys that can't change: a string, a number, a frozen/immutable object. And no, it has no order A hash map scatters keys by their hash, not by when you added them, so iteration order is whatever the buckets happen to hold. Python is the exception you've probably been relying on: since 3.7, a dict preserves insertion order as a language guarantee. Java's HashMap promises nothing of the sort. If you need order, use the type that promises it. Three rules - Keys that don't change. - A hash that spreads keys out. - Enough room that the map isn't constantly resizing. Get those right and it behaves like magic. Get them wrong and it quietly becomes a list. A hash map isn't magic and it isn't a tree — it's an array that already knows where you put things. What's the worst hash-map bug you've hit — the key that vanished, or the resize that froze your app? Tell me in the comments, I read them. --- I make Vlad's Stack — how the tools you use every day actually work, for people who write code. The full video walkthrough is above.

  7. Updated triage guidelines and process for our AI policy In May, we updated the guidelines for using AI tools when contributing to CPython. We now have an updated process for enforcing that the policy is upheld. This post explains how the process works and what to expect. What's New Triagers and core team members can now apply one of two labels to an issue or PR on the CPython repository. When they do, a bot posts a comment that explains why and links to the guidelines. | Label | What happens | |---|---| | AI-needs-explanation | The bot asks the author to explain the contribution in their own words. The issue or PR stays open. | | AI-guidelines-not-met | The bot explains that the contribution doesn't meet the guidelines, and closes it. | Using AI tools is not, on its own, a reason for either label. Disclosing that you used AI tools is still appreciated, and it won't get your contribution labeled. If you're a contributor If your issue or PR gets AI-needs-explanation, please reply in your own words. Explain what the change does, or what the issue is about, and describe how you checked that it is correct. If that resolves the concern, the label is removed and the review carries on as usual. If there's no reply, or the reply doesn't address the concern, it may be closed with AI-guidelines-not-met. If your issue or PR gets AI-guidelines-not-met, it is closed, but the conversation is not locked. If you think we misunderstood your contribution, reply on the issue or PR. You're welcome to explain your approach or ask questions about the guidelines. Either way, a label is a request about one contribution. It is not a judgment about you. The guidelines page now has a section on what each label means and how to respond. If you're a triager or core team member - Start with AI-needs-explanation. Use AI-guidelines-not-met only for clear cases, such as a low-effort submission where the author is unlikely to engage with feedback. - Keep one label at a time. When escalating, remove AI-needs-explanation and add AI-guidelines-not-met. - Prefer the labels over closing by hand. Authors get the same explanation every time, and we can filter on the labels later to see how often they're used and whether they help. - If in doubt, ask a core team member. The full guidance is in the devguide under Applying the AI policy labels, and both labels are listed on the labels page. The pull requests - bedevere#722: the bot that posts the comments and closes issues and PRs. - devguide#1933: documents the labels. - devguide#1947: explains the process to contributors and adds the triage guidance, closing devguide#1935. Thank you Thank you to the core team members who discussed this during the Core Python Sprint and on Discord, and thanks to those who collaborated and contributed to the various PRs to implement this process change: @zanie @willingc @savannahostrowski @hugovk @Stanfromireland @zware @AlexWaygood What's next We will continue reviewing and updating this process as we learn what works. If something about the labels or the process doesn't work for you, as a contributor or as a maintainer, please reply here or open an issue on the devguide repo. Thanks!

    20 likes
  8. Most of them have been variations on the CA bundle (which is vendored in certifi which is vendored into pip which is vendored into python 😬). I *think* the most recent one should have the "block intermediates by SPKI" change (landed in dev, and being backported). cpython#160042

    2 likes
  9. > I would expect such a project to get continual updates without PEPs. Just for completeness: the original JIT PR was merged quite enthusiastically and only after the fact it was pointed out that it should have went through a PEP process [1]. The following PEP 744 was of the “informational” type, and the just-withdrawn PEP 836 was the first “standards track” PEP, which means that (in my understanding) it’s technically the first actual PEP that the SC would formally comment on. So in a way it’s not “can we update the JIT” PEP, but the first real “does SC want this JIT as proposed” PEP. That said, I do agree with most of what you said, especially the acceptance criteria part. The issue I have and find it hard to resolve is that since the code was already on main, the feature was already in “sunk cost” mode. It’s hard when the code is already on main and another 3 optimization PRs are always there behind the corner, but it needs to be possible for SC to say “we decided it’s not actually worth all this complexity for <some small number>% speed improvement” (I’m not commenting whether in this particular case it’d have been a right call), and the PEP (if not withdrawn) was the way to exercise that control. [1] cpython#113465

  10. CPython This is the source code for CPython, an official implementation of Python. Explore it when you're curious about how the language works internally. It's more useful after you've learned the basics of Python. GitHub repo - python/cpython

    2 likes · 6 views

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

3
PackageVersionDownloads · 30dTrend · 30d
———
customv0.1.1
v0.1.1
≥362
v0.1
≥27

README badges

Embed a badge in your README or website.

  • python/cpython reached #1 in daily stars on gitwal · Feb 12, 2017
  • python/cpython reached #3 in weekly stars on gitwal · Week of Feb 13, 2017
  • python/cpython reached #1 in daily forks on gitwal · Feb 13, 2017
  • python/cpython reached #3 in daily PR authors on gitwal · Jun 10, 2017
Live mentions · last 30 days
python/cpython mentions in the last 30 days