Making a Python release is a thrilling and crazy process. You’ve heardthe expression “herding cats”? Imagine trying to also saddle thosepurring little creatures up, and ride them into town, with some of theirbuddies firmly attached to your bare back, anchored by newly sharpenedclaws. At least they’re cute, you remind yourself.
Actually, no, that’s a slight exaggeration 😉 The Python releaseprocess has steadily improved over the years and now, with the help of ouramazing community, is really not too difficult. This PEP attempts tocollect, in one place, all the steps needed to make a Python release.Most of the steps are now automated or guided by automation, so manuallyfollowing this list is no longer necessary.
As a release manager there are a lot of resources you’ll need to access.Here’s a hopefully-complete list.
Python releases before 3.14 are digitally signed with GPG; for these you’llneed a key, which hopefully will be on the “web of trust” with at least one ofthe other release managers.
Note
GPG instructions in this PEP can be ignored for Python 3.14 andlater. SeePEP 761 for details.
downloads.nyc1.psf.io, the server that hosts download files; anddocs.nyc1.psf.io, the server that hosts the documentation.If you’re reading this, you probably already have this–the firsttask of any release manager is to draft the release schedule. Butin case you just signed up… sucker! I mean, uh, congratulations!
python-cabal. Bug Barry about this.@python.org email address that you will use to sign your releaseswith. Askpostmaster@ for an address; you can either get a fullaccount, or a redirecting alias + SMTP credentials to send email fromthis address that looks legit to major email providers.There are several types of releases you will need to make. These include:
alphabeginbeta, also known asbeta1, also known asnewbranchbeta2+releasecandidate1releasecandidate2+finalnewbranchbeginbugfixmodebeginsecurity-onlymodeend-of-lifeSome of these release types actually involve more thanone release branch. In particular, anew branch is that point in therelease cycle when a new feature release cycle begins. Under the currentorganization of the CPython Git repository, themain branch is alwaysthe target for new features. At some point in the release cycle of thenext feature release, anew branch release is made which creates anew separate branch for stabilization and later maintenance of thecurrent in-progress feature release (3.n.0) and themain branch is modifiedto build a new version (which will eventually be released as3.n+1.0).While thenew branch release step could occur at one of several pointsin the release cycle, current practice is for it to occur at feature codecutoff for the release which is scheduled for the first beta release.
In the descriptions that follow, steps specific to release types arelabeled accordingly, for now,new branch andfinal.
Here are the steps taken to make a Python release. Some steps are morefuzzy than others because there’s little that can be automated (e.g.writing the NEWS entries). Where a step is usually performed by AnExpert, the role of that expert is given. Otherwise, assume the step isdone by the Release Manager (RM), the designated person performing therelease. The roles and their current experts are:
Note
It is highly recommended that the RM contact the Experts the daybefore the release. Because the world is round and everyone livesin different timezones, the RM must ensure that the release tag iscreated in enough time for the Experts to cut binary releases.
You should not make the release public (by updating the website andsending announcements) before all experts have updated their bits.In rare cases where the expert for Windows or Mac is MIA, you may adda message “(Platform) binaries will be provided shortly” and proceed.
We use the following conventions in the examples below. Where a releasenumber is given, it is of the form3.X.YaN, e.g. 3.13.0a3 for Python 3.13.0alpha 3, where “a” == alpha, “b” == beta, “rc” == release candidate.
Release tags are namedv3.X.YaN. The branch name for minor releasemaintenance branches is3.X.
As much as possible, the release is automated and guided by therun_release.py script, which is available in a separate repository:python/release-tools. This helps by automating many of the following steps,and guides you to perform some manual steps.
You probably need to coordinate with other people around the world.This communication channel is where we’ve arranged to meet.
Go tohttps://github.com/python/cpython/issues and look for any openbugs that can block this release. You’re looking at two relevant labels:
Review the release blockers and either resolve them, bump them down todeferred, or stop the release and ask for community assistance. Ifyou’re making a final or candidate release, do the same with any opendeferred.
Go tohttps://buildbot.python.org/all/#/release_status
Look at the buildbots for the releaseyou’re making. Ignore any that are offline (or inform the community sothey can be restarted). If what remains are (mostly) green buildbots,you’re good to go. If you have non-offline red buildbots, you may wantto hold up the release until they are fixed. Review the problems anduse your judgement, taking into account whether you are making an alpha,beta, or final release.
On a fork of the CPython repository on GitHub, create a release branchwithin it (called the “release clone” from now on). You can use the sameGitHub fork you use for CPython development. Using the standard setuprecommended in thePython Developer’s Guide,your fork would be referredto asorigin and the standard CPython repo asupstream. You willuse the branch on your fork to do the release engineering work, includingtagging the release, and you will use it to share with the other expertsfor making the binaries.
For afinal orrelease candidate 2+ release, if you are goingto cherry-pick a subset of changes for the next rc or final from all thosemerged since the last rc, you should create a releaseengineering branch starting from the most recent release candidate tag,i.e.v3.8.0rc1. You will then cherry-pick changes from the standardrelease branch as necessary into the release engineering branch andthen proceed as usual. If you are going to take all of the changessince the previous rc, you can proceed as normal.
gitstatus).blurbrelease<version> specifying the version number(e.g.blurbrelease3.4.7rc1). This merges all the recent newsblurbs into a single file marked with this release’s version number.Lib/pydoc-topics.py.While still in theDoc directory, run:
makepydoc-topicscpbuild/pydoc-topics/topics.py../Lib/pydoc_data/topics.py
pydoc_topics.py(and any fixes you made in the docs).autoconf using the currently accepted standard versionin caseconfigure or other Autoconf-generated files were lastcommitted with a newer or older version and may contain spurious orharmful differences. Currently, Autoconf 2.71 is our de facto standard.if there are differences, commit them.SOURCE_URI inDoc/tools/extensions/pyspecific.pypoints to the right branch in the Git repository (main or3.X).For anew branch release, change the branch in the file frommainto the new release branch you are about to create (3.X)..../release-tools/release.py--bump3.X.YaNReminder:X,Y, andN should be integers.a should be one ofa,b, orrc (e.g.3.4.3rc1).Forfinal releases omit theaN (3.4.3). For the firstrelease of a new versionY should be0 (3.6.0).
This automates updating various release numbers, but you will have tomodify a few files manually. If your$EDITOR environment variable isset up correctly,release.py will pop up editor windows with the filesyou need to edit.
Review the blurb-generatedMisc/NEWS file and edit as necessary.
release.py--bumpdoesn’t check in its changes for you.)Doc/whatsnew/3.X.rst to include the actual release date; e.g. “Python2.5 was released on August 1, 2003.” There’s no need to edit this foralpha or beta releases.gitstatus in this directory.You should not see any files, i.e., you better not have any uncommittedchanges in your working directory.
3.X.YaN:.../release-tools/release.py--tag3.X.YaNThis executes agittag command with the-s option so that therelease tag in the repo is signed with your GPG key. When promptedchoose the private key you use for signing release tarballs etc.
# Do a dry run first.gitpush--dry-run--tagsorigin# Make sure you are pushing to your GitHub fork,# *not* to the main python/cpython repo!gitpush--tagsorigin
The resulting artifacts will be attached to the summary page of the GitHubworkflow. Once the source tarball is available, download and unpack it to makesure things look reasonable, there are no stray .pyc files, etc.
If the tests pass, then you can feel good that the tarball isfine. If some of the tests fail, or anything else about thefreshly unpacked directory looks weird, you better stop now andfigure out what the problem is.
Warning
STOP: at this point you must receive the “green light” from other expertsin order to create the release. There are things you can do while you waitthough, so keep reading until you hit the next STOP.
.azure-pipelines/windows-release/,currently set up athttps://dev.azure.com/Python/cpython/_build?definitionId=21.The build process runs in multiple stages, with each stage’s output beingavailable as a downloadable artifact. The stages are:
After the uploads are complete, the WE copies the generated hashes fromthe build logs and emails them to the RM. The Windows Store packages areuploaded manually tohttps://partner.microsoft.com/dashboard/home by theWE.
scp orrsync all the files built by the build-release workflowto your home directory ondownloads.nyc1.psf.io, along with anysignatures, SBOMs, etc.While you’re waiting for the files to finish uploading, you can continueon with the remaining tasks. You can also ask folks on Discordand/ordiscuss.python.org to download the files as they finish uploadingso that they can test them on their platforms as well.
downloads.nyc1.psf.io and move all the files in placeover there. Our policy is that every Python version gets its owndirectory, but each directory contains all releases of that version.downloads.nyc1.psf.io,cd/srv/www.python.org/ftp/python/3.X.Ycreating it if necessary. Make sure it is owned by groupdownloadsand group-writable.Make sure they are world readable. They should also be groupwritable, and group-owned bydownloads.
gpg--verify to make sure they got uploaded intact./srv/www.python.org/ftp/python/doc/3.X.Y[rcA], creating the directoryif necessary, and adapt the “current” symlink in.../doc to point tothat directory. Note though that if you’re releasing a maintenancerelease for an older version, don’t change the current link./srv/docs.python.org/release/3.X.Y[rcA] ondocs.nyc1.psf.io. Make sure the files are in groupdocs and aregroup-writeable.curl-XPURGEhttps://www.python.org/ftp/python/3.12.0/Python-3.12.0.tar.xz
You should always purge the cache of the directory listing as peopleuse that to browse the release files:
curl-XPURGEhttps://www.python.org/ftp/python/3.12.0/
Make sure the md5 checksums match. Then unpack the tarball,and do a clean make test:
makedistclean./configuremaketestTo ensure that the regression test suite passes. If not, youscrewed up somewhere!
Warning
STOP and confirm:
If green, it’s time to merge the release engineering branch back intothe main repo.
Settings|Branches page:https://github.com/python/cpython/settings/branches
“Edit” the settings for the branch you’re releasing on.This will load the settings page for that branch.Uncheck the “Include administrators” box and press the“Save changes” button at the bottom.
# Pristine copy of the upstream repo branchgitclonegit@github.com:python/cpython.gitmergecdmerge# Checkout the correct branch:# 1. For feature pre-releases up to and including a# **new branch** release, i.e. alphas and first beta# do a checkout of the main branchgitcheckoutmain# 2. Else, for all other releases, checkout the# appropriate release branch.gitcheckout3.X# Fetch the newly created and signed tag from your clone repogitfetch--tagsgit@github.com:your-github-id/cpython.gitv3.X.YaN# Merge the temporary release engineering branch back intogitmerge--no-squashv3.X.YaNgitcommit-m'Merge release engineering branch'
gitcheckout-b3.XDo any steps needed to setup the new release branch, including:
README.rst, change all references frommain tothe new branch, in particular, GitHub repo URLs..../release-tools/release.py--done3.X.YaNREADME.rstandinclude/patchlevel.h files to ensure they now reflectthe desired post-release values for on-going development.The patchlevel should be the release tag with a+.Also, if you cherry-picked changes from the standard releasebranch into the release engineering branch for this release,you will now need to manually remove each blurb entry fromtheMisc/NEWS.d/next directory that was cherry-pickedinto the release you are working on since that blurb entryis now captured in the mergedx.y.z.rst file for the newrelease. Otherwise, the blurb entry will appear twice inthechangelog.html file, once underPythonnext and againunderx.y.z.gitcommit-m'Post release updates'main branch to start development for thefollowing feature release. When finished, themainbranch will now build PythonX.Y+1.main up to be the next release, i.e. X.Y+1.a0:gitcheckoutmain.../release-tools/release.py--bump3.9.0a0README.rstDoc/tutorial/interpreter.rst (two references to ‘[Pp]ython3x’,one to ‘Python 3.x’, also make the date in the banner consistent).Doc/tutorial/stdlib.rst andDoc/tutorial/stdlib2.rst, whichhave each one reference to ‘[Pp]ython3x’.whatsnew/3.x.rst file (with the comment near the topand the toplevel sections copied from the previous file) andadd it to the toctree inwhatsnew/index.rst. But beware thatthe initialwhatsnew/3.x.rst checkin from previous releasesmay be incorrect due to the initial midstream change toblurbthat propagates from release to release! Help break the cycle: ifnecessary make the following change:-For full details, see the :source:`Misc/NEWS` file.+For full details, see the :ref:`changelog <changelog>`.
configure.ac and re-runautoconf.SOURCE_URI inDoc/tools/extensions/pyspecific.pypoints tomain.python38:lsPC/pyconfig.h.inPCbuild/rt.bat|xargssed-i's/python3\(\.\?\)[0-9]\+/python3\19/g'
bug.yml andcrash.yml issue templates in.github/ISSUE_TEMPLATE/ to add the new branch to the“versions” dropdown.gitstatusgitadd...gitcommit-m'Bump to 3.9.0a0'gitstatus in this directory.You should not see any files, i.e., you better not have any uncommittedchanges in your working directory.
# Do a dry run first.# For feature pre-releases prior to a **new branch** release,# i.e. a feature alpha release:gitpush--dry-run--tagsgit@github.com:python/cpython.gitmain# If it looks OK, take the plunge. There's no going back!gitpush--tagsgit@github.com:python/cpython.gitmain# For a **new branch** release, i.e. first beta:gitpush--dry-run--tagsgit@github.com:python/cpython.git3.Xgitpush--dry-run--tagsgit@github.com:python/cpython.gitmain# If it looks OK, take the plunge. There's no going back!gitpush--tagsgit@github.com:python/cpython.git3.Xgitpush--tagsgit@github.com:python/cpython.gitmain# For all other releases:gitpush--dry-run--tagsgit@github.com:python/cpython.git3.X# If it looks OK, take the plunge. There's no going back!gitpush--tagsgit@github.com:python/cpython.git3.X
Branchprotectionrulefor the newly created branch (3.X). Look at the values for the previousrelease branch (3.X-1) and use them as a template.https://github.com/python/cpython/settings/branchesAlso, add3.x andneedsbackportto3.X labels to the GitHub repo.https://github.com/python/cpython/labels
Settings|Branch page:https://github.com/python/cpython/settings/branches
“Edit” the settings for the branch you’re releasing on.Re-check the “Include administrators” box and press the“Save changes” button at the bottom.
Now it’s time to twiddle the website. Almost none of this is automated, sorry.
To do these steps, you must have the permission to edit the website. If youdon’t have that, ask someone onpydotorg@python.org for the properpermissions.
The easiest thing is probably to copy fields from an existingPython release “page”, editing as you go.
You can useMarkdown orreStructured Textto describe your release. The former is less verbose, while the latter has niftyintegration for things like referencing PEPs.
Leave the “Release page” field on the form empty.
Your friend and mine, Georg Brandl, made a lovely toolcalledadd_to_pydotorg.py. You can find it in thepython/release-tools repo (next torelease.py). You run thetool ondownloads.nyc1.psf.io, like this:
AUTH_INFO=<username>:<python.org-api-key>pythonadd_to_pydotorg.py<version>
This walks the correct download directory for<version>,looks for files marked with<version>, and populatesthe “Release Files” for the correct “release” on the website with these files. Note that clears the “Release Files”for the relevant version each time it’s run. You may runit from any directory you like, and you can run it asmany times as you like if the files happen to change.Keep a copy in your home directory on dl-files andkeep it fresh.
If new types of files are added to the release, someone will need toupdateadd_to_pydotorg.py so it recognizes these new files.(It’s best to updateadd_to_pydotorg.py when file typesare removed, too.)
The script will also sign any remaining files that were notsigned with Sigstore until this point. Again, if this happens,do use your@python.org address for this process. More info:https://www.python.org/downloads/metadata/sigstore/
curl-XPURGEhttps://www.python.org/downloads/release/python-XXX/
main and new releasebranches to identify and backport any merges that might have been madeto themain branch during the release engineering phase and thatshould be in the release branch.main and new releasebranches and that the release branch is properly protected (no directpushes, etc).You’ve just made a Python release!
Under current policy, a release branch normally reaches end-of-life statusfive years after its initial release. The policy is discussed in more detailin thePython Developer’s Guide.When end-of-life is reached, there are a number of tasks that need to beperformed either directly by you as release manager or by ensuring someoneelse does them. Some of those tasks include:
gitfetchupstreamgittag--sign-m'Final head of the former 3.3 branch'3.3upstream/3.3gitpushupstreamrefs/tags/3.3
gitpushupstream--delete3.3# or perform from GitHub Settings page
downloads-active-releases entry. Strip out the relevantparagraph of HTML for your release. (You’ll probably have to do thecurl-XPURGEtrick to purge the cache if you want to confirm you made the change correctly.)needsbackportto label for the retired versionWindows has a MSI installer, various flavors of Windows have“special limitations”, and the Windows installer also packsprecompiled “foreign” binaries (Tcl/Tk, expat, etc).
The installer is tested as part of the Azure Pipeline. In the past,those steps were performed manually. We’re keeping this for posterity.
Concurrent with uploading the installer, the WE installs Pythonfrom it twice: once into the default directory suggested by theinstaller, and later into a directory with embedded spaces in itsname. For each installation, the WE runs the full regression suitefrom a DOS box, and both with and without -0. For maintenancerelease, the WE also tests whether upgrade installations succeed.
The WE also triesevery shortcut created under Start -> Menu -> thePython group. When trying IDLE this way, you need to verify thatHelp -> Python Documentation works. When trying pydoc this way(the “Module Docs” Start menu entry), make sure the “StartBrowser” button works, and make sure you can search for a randommodule (like “random” <wink>) and then that the “go to selected”button works.
It’s amazing how much can go wrong here – and even more amazinghow often last-second checkins break one of these things. Ifyou’re “the Windows geek”, keep in mind that you’re likely theonly person routinely testing on Windows, and that Windows issimply a mess.
Repeat the testing for each target architecture. Try both anAdmin and a plain User (not Power User) account.
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0101.rst
Last modified:2025-12-05 21:44:42 GMT