{"slug": "hello-python-3-15-goodbye-3-10-and-now-i-can-use-3-11", "title": "Hello Python 3.15, goodbye 3.10, and now I can use 3.11", "summary": "Python 3.15 has been released, adding PEP 810 explicit lazy imports, PEP 798 unpacking in comprehensions, and a new Tachyon sampling profiler in the standard library as profiling.sampling, while the existing cProfile tracing profiler is renamed profiling.tracing. Miguel Grinberg's benchmarks against the 3.15.0rc3 release found performance mostly unchanged from Python 3.14, and the release marks the end of support for Python 3.10.", "body_md": "Python 3.15 [is here](https://www.python.org/downloads/release/python-3150/)! Here's the essential [What's new in Python 3.15](https://docs.python.org/3.15/whatsnew/3.15.html) guide, this year curated by 3.14 and 3.15 release manager Hugo van Kemenade. As someone who builds open source software that works in every supported Python version, I get to say goodbye to Python 3.10 and start embracing features from Python 3.11.\n\nFirst, some highlights from Python 3.15.\n\nI'm most excited about [Explicit lazy imports](https://docs.python.org/3.15/whatsnew/3.15.html#pep-810-explicit-lazy-imports) from [PEP 810](https://peps.python.org/pep-0810/). These let you import modules like so:\n\n``` python\nlazy import json\nlazy from pathlib import Path\n```\n\nThe module will then be imported the first time those symbols are used in the code that follows.\n\nMy [LLM](https://llm.datasette.io/) CLI tool implements plugins. I had to [recommend manual lazy imports](https://llm.datasette.io/en/stable/plugins/advanced-model-plugins.html#tip-lazily-load-expensive-dependencies) in the plugin author documentation, because simply installing plugins that used heavy dependencies such as `sentence_transformers` could add multiple seconds to every invocation of the `llm` command. Lazy imports directly address this startup cost.\n\nThe new `[*sublist for sublist in lists]` unpacking syntax is neat. Here's an illustration [lifted from the documentation](https://docs.python.org/3.15/whatsnew/3.15.html#pep-798-unpacking-in-comprehensions):\n\n```\nlists = [[1, 2], [3, 4], [5]]\n[*L for L in lists]\n# [1, 2, 3, 4, 5]\n# equivalent to [x for L in lists for x in L]\n\nsets = [{1, 2}, {2, 3}, {3, 4}]\n{*s for s in sets}\n# {1, 2, 3, 4}\n# equivalent to {x for s in sets for x in s}\n\ndicts = [{'a': 1}, {'b': 2}, {'a': 3}]\n{**d for d in dicts}\n# {'a': 3, 'b': 2}\n# equivalent to {k: v for d in dicts for k,v in d.items()}\n```\n\n[PEP 798: Unpacking in Comprehensions](https://peps.python.org/pep-0798/) by Adam Hartz and Erik Demaine proposed this as an extension of the unpacking syntax [from PEP 448](https://peps.python.org/pep-0448/), which landed [in Python 3.5](https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations) back in 2015.\n\nPython's existing tracing profiler has been renamed from `cProfile` to [profiling.tracing](https://docs.python.org/3.15/library/profiling.tracing.html#module-profiling.tracing), and has been joined by an exciting new sampling profiler called Tachyon, aka [profiling.sampling](https://docs.python.org/3.15/whatsnew/3.15.html#tachyon-high-frequency-statistical-sampling-profiler).\n\nSampling profilers work by periodically capturing stack traces from running processes, then using those to calculate which functions have the most time spent inside of them. They're very low overhead and can provide a deep insight into running systems, without needing to instrument those systems first.\n\nI've [previously used tools](https://simonwillison.net/tags/profiling/) like [py-spy](https://github.com/benfred/py-spy) for this. Now this capability is baked into the standard library.\n\nIf you have the PID of an existing Python 3.15 process you can start gathering samples by running this (I had to use `sudo` on macOS):\n\n```\nsudo uv run --python 3.15 python -m profiling.sampling attach 1234\n```\n\nInteract with the main program a bit, then hit Ctrl+C to stop gathering and see a report, which looks something like this:\n\nIt can also produce [flame graphs like this](https://simonw.github.io/research/python-3-15-web-edit-profiling/artifacts/flamegraph.html) and [heat maps like this](https://simonw.github.io/research/python-3-15-web-edit-profiling/artifacts/heatmap/index.html), and can even output a format that can be viewed in the Firefox profiler UI.\n\nMiguel Grinberg investigated [How fast is Python 3.15?](https://blog.miguelgrinberg.com/post/how-fast-is-python-3-15) the other day against the 3.15.0rc3 release and found performance to be mostly the same as 3.14, with the exception of the experimental JIT. The JIT gave a 1.2x boost over the standard 3.15 interpreter (on a Fibonacci test), the first time Miguel observed a meaningful JIT performance increase.\n\nMiguel's [explorations of 3.14](https://blog.miguelgrinberg.com/post/python-3-14-is-here-how-fast-is-it) last year found significant boosts against Python 3.13 across many more categories.\n\nI used the following sequence of commands to get `uv` to use Python 3.15 on my Mac. First I had to upgrade `uv` itself to ensure it knew about the new release:\n\n```\nuv self update\n```\n\nThen I installed the stable 3.15 release:\n\n```\nuv python install 3.15\n```\n\nAnd told `uv` that I wanted that to be the default for every `uv run` command, unless the project itself pinned a different version:\n\n```\nuv python pin --global 3.15\n```\n\nNow the following reports 3.15 as the version:\n\n```\nuv run python --version\n```\n\nI already have a bunch of tools installed using `uv tool install`. I reinstalled them to use Python 3.15 like this:\n\n```\nuv tool upgrade datasette --python 3.15\n```\n\nThen I can confirm the version like this:\n\n```\ndatasette --get /-/versions.json | jq\n```\n\nThe release of 3.15 means that Python 3.10 (released in 2021) is officially [no longer supported](https://devguide.python.org/versions/).\n\nMy policy for my main open source projects is to keep them working in every supported Python version. I can now drop 3.10 and start using features that were introduced in 3.11.\n\nHere's the [What's new in Python 3.11](https://docs.python.org/3/whatsnew/3.11.html) document. I finally get to rely on [tomllib](https://docs.python.org/3/library/tomllib.html#module-tomllib) being available!\n\nDatasette makes extensive use of `asyncio`. 3.11 adds [TaskGroup](https://docs.python.org/3/library/asyncio-task.html#asyncio.TaskGroup) and [timeout()](https://docs.python.org/3/library/asyncio-task.html#asyncio.timeout), and [several more](https://docs.python.org/3/whatsnew/3.11.html#asyncio) new features.\n\n`datetime.UTC` is a new in 3.11 alias for `datetime.timezone.utc`.\n\nPython's SQLite support got some significant upgrades in 3.11, including:\n\n`step()`, `value()`, `inverse()`, and `finalize()` methods.`BLOB` binary values stored in a SQLite row.`set_authorizer(None)` to remove a SQLite authorizer. Datasette had a workaround for this!\nPlus a whole bunch of [improvements to Python's typing system](https://docs.python.org/3/whatsnew/3.11.html#new-feat-related-type-hints-311).\n\nI plan to land Python 3.11 improvements to a bunch of my projects over the next few weeks. Here's [my first Datasette PR](https://github.com/simonw/datasette/pull/2998) removing that `set_authorizer(None)` workaround.\n\nI've also updated my [click-app](https://github.com/simonw/click-app), [datasette-plugin](https://github.com/simonw/datasette-plugin), [python-lib](https://github.com/simonw/python-lib), and [llm-plugin](https://github.com/simonw/llm-plugin) cookiecutter templates to test against Python 3.11 through Python 3.15.\n\nTags: [open-source](https://simonwillison.net/tags/open-source), [profiling](https://simonwillison.net/tags/profiling), [python](https://simonwillison.net/tags/python), [sqlite](https://simonwillison.net/tags/sqlite), [uv](https://simonwillison.net/tags/uv)", "url": "https://wpnews.pro/news/hello-python-3-15-goodbye-3-10-and-now-i-can-use-3-11", "canonical_source": "https://simonwillison.net/2026/Oct/12/python-3-15/", "published_at": "2026-10-12 18:15:38+00:00", "updated_at": "2026-10-12 18:20:20.858993+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Python 3.15", "Python 3.10", "Python 3.11", "Hugo van Kemenade", "Miguel Grinberg", "Adam Hartz", "Erik Demaine", "Tachyon"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/hello-python-3-15-goodbye-3-10-and-now-i-can-use-3-11", "markdown": "https://wpnews.pro/news/hello-python-3-15-goodbye-3-10-and-now-i-can-use-3-11.md", "text": "https://wpnews.pro/news/hello-python-3-15-goodbye-3-10-and-now-i-can-use-3-11.txt", "jsonld": "https://wpnews.pro/news/hello-python-3-15-goodbye-3-10-and-now-i-can-use-3-11.jsonld"}}