PEP: 2026 Title: Calendar versioning for Python Author: Hugo van
Kemenade Discussions-To:
https://discuss.python.org/t/pep-2026-calendar-versioning-for-python/55782
Status: Draft Type: Process Created: 11-Jun-2024 Python-Version: 3.26
Post-History: 14-Jun-2024

Abstract

This PEP proposes updating the versioning scheme for Python to include
the calendar year.

Calendar Versioning (CalVer) makes everything easier to translate into
calendar time rather than counting versions and looking up when they
will be (or were) released:

-   The support lifecycle is clear, making it easy to see when a version
    was first released.
-   Deprecations are easier to manage for maintainers and users.
-   It's easier to work out when a version will reach end of life (EOL).
-   It helps people, especially new learners, understand how old their
    installation is.
-   It's easier to reason about which versions of Python to support for
    libraries and applications.

Starting with what would have been Python 3.15, the version is
3.YY.micro where YY is the year of initial release:

-   Python 3.26 will be released in 2026 instead of Python 3.15. EOL is
    five years after initial release, therefore Python 3.26 will reach
    EOL in 2031.
-   Python 3.27 will be released in 2027, and so on.

Motivation and rationale

In 2019, we adopted an annual release cycle with PEP 602, which opened
the door for calendar versioning:

  Adopting an annual release calendar allows for natural switching to
  calendar versioning, for example by calling Python 3.9 “Python 3.20”
  since it’s released in October ‘20 and so on (“Python 3.23” would be
  the one released in October ‘23).

  While the ease of switching to calendar versioning can be treated as
  an advantage of an annual release cycle, this PEP does not advocate
  for or against a change in how Python is versioned. Should the annual
  release cycle be adopted, the versioning question will be dealt with
  in a separate PEP.

This is that PEP.

Current scheme

From the General Python FAQ: faq-version-numbering-scheme

  Python versions are numbered "A.B.C" or "A.B":

  -   A is the major version number -- it is only incremented for really
      major changes in the language.
  -   B is the minor version number -- it is incremented for less
      earth-shattering changes.
  -   C is the micro version number -- it is incremented for each bugfix
      release.

Python predates SemVer

Semantic Versioning (SemVer) is a popular scheme which aims to
communicate the intent of a release (though it doesn't always succeed).

  Given a version number MAJOR.MINOR.PATCH, increment the:

  1.  MAJOR version when you make incompatible API changes
  2.  MINOR version when you add functionality in a backward compatible
      manner
  3.  PATCH version when you make backward compatible bug fixes

People often assume Python follows SemVer and complain about breaking
changes in feature releases. But Python predates SemVer by at least 15
years: the SemVer spec was introduced in 2009 and the bespoke Python
scheme was added to source control in 1994 for the 1.0 release.

If Python adopted SemVer, that would imply a new major bump every year
when we remove deprecations.

Instead of SemVer, however, some projects have adopted another
versioning scheme based on the calendar.

Calendar versioning

With Calendar Versioning (CalVer), you include some element of the date
in the version number. For example, Ubuntu and Black use the year and
month -- Ubuntu 24.04 came out in April 2024; pip and PyCharm use only
the year.

+-------------+-------------+----------------+------------------+
| Ubuntu      | Black       | pip            | PyCharm          |
+=============+=============+================+==================+
| YY.0M.micro | YY.MM.micro | YY.minor.micro | YYYY.minor.micro |
+-------------+-------------+----------------+------------------+
| 23.04       | 23.12.1     | 23.3           | 2023.3.5         |
| 23.10       | 24.1.0      | 23.3.1         | 2024.1           |
| 24.04       | 24.1.1      | 23.3.2         | 2024.1.1         |
| 24.10       | 24.2.0      | 24.0           | 2024.1.2         |
+-------------+-------------+----------------+------------------+

And here are some programming language standards, all using some form of
the year:

+-----------+-------+----+-----+-----------+----------------+
| Ada       | Algol | C  | C++ | Fortran   | ECMAScript     |
|           |       |    |     |           | aka JavaScript |
+===========+=======+====+=====+===========+================+
| YY / YYYY | YY    | YY | YY  | YY / YYYY | YYYY           |
+-----------+-------+----+-----+-----------+----------------+
| 83        | 58    | 89 | 98  | 66        | 2020           |
| 95        | 60    | 99 | 03  | 90        | 2021           |
| 2012      | 68    | 11 | 11  | 2003      | 2022           |
| 2022      |       | 23 | 23  | 2023      | 2023           |
+-----------+-------+----+-----+-----------+----------------+

Annual release cadence

Since 2019, we've made a release each year:

-   3.15.0 will be released in 2026
-   3.16.0 will be released in 2027
-   3.17.0 will be released in 2028
-   3.18.0 will be released in 2029
-   3.19.0 will be released in 2030

This is sort of calendar-based, it’s just that it’s offset by 11 years.

CalVer for Python

The simplest CalVer option would be to stick with major version 3, and
encode the year in the minor version:

-   3.26.0 will be released in 2026
-   3.27.0 will be released in 2027
-   3.28.0 will be released in 2028
-   3.29.0 will be released in 2029
-   3.30.0 will be released in 2030

For example, 3.26 will be released in 2026. It makes it obvious when a
release first came out.

Clarity of deprecation removal

Warnings for deprecations often mention the version they will be removed
in. For example:

  DeprecationWarning: 'ctypes.SetPointerType' is deprecated and slated
  for removal in Python 3.15

However, once aware of CalVer, it is immediately obvious from the
warning how long you have to take action:

  DeprecationWarning: 'ctypes.SetPointerType' is deprecated and slated
  for removal in Python 3.26

Clarity of support lifecycle

Right now, it’s a little tricky to work out when a release is
end-of-life. First you have to look up when it was initially released,
then add 5 years:

  "When will Python 3.11 be EOL?"

  "Well, let's see... PEP 664 is the 3.11 release schedule, it says 3.11
  was released in 2022, EOL after 5 years, so 2022 + 5 = 2027."

But if the initial release year is right there in the version, it’s much
easier:

  "When will Python 3.26 be EOL?"

  "26 + 5 = [20]31"

Clarity of installation age

With the year in the version, it’s easier to work out how old your
installation is. For example, with the current scheme, if you're using
Python 3.15 in 2035, it's not immediately clear that it was first
released in 2026 (and has been EOL since 2031).

With knowledge of CalVer, if you're using Python 3.26 in 2035, it's
clear it was first released nine years ago and it's probably time to
upgrade.

This can help prompt people to switch to supported releases still under
security support, and help in teaching new users who may have older
installations.

Clarity of version support

CalVer makes it easier to reason about which versions of Python to
support.

For example, without CalVer, setting your minimum compatible Python
version to 3.19 in 2031 sets an aggressive assumption regarding version
adoption and support.

However, with CalVer, this is more obvious if setting the minimum to
3.30 in 2031. For wider support, perhaps you prefer setting it to 3.26.

Similarly, library maintainers supporting all CPython upstream versions
need to test against five versions (or six including the pre-release).

For example, in 2030, the supported versions without CalVer would be:

-   3.15, 3.16, 3.17, 3.18, 3.19

With CalVer they would be:

-   3.26, 3.27, 3.28, 3.29, 3.30

A maintainer can see at a glance which versions are current and need
testing.

Non-goals

Like the current scheme, only the micro version will be incremented for
bug fix and security releases, with no change to the major and minor.
For example:

                                     Current scheme   Proposed 3.YY.micro
  ---------------------------------- ---------------- ---------------------
  Initial release (Oct ’26)          3.15.0           3.26.0
  1st bugfix release (Dec ’26)       3.15.1           3.26.1
  2nd bugfix release (Feb ’27)       3.15.2           3.26.2
  ...                                ...              ...
  Final security release (Oct ’31)   3.15.17          3.26.17

No change to PEP 602 (Annual Release Cycle for Python):

-   No change to the 17 months to develop a feature version: alphas,
    betas and release candidates.
-   No change to the support duration: two years of full support and
    three years of security fixes.
-   No change to the annual October release cadence.

Specification

Python versions are numbered 3.YY.micro where:

-   3 is the major version number – it is always 3.
-   YY is the minor version number
    -   it is the short year number: {year} - 2000.
-   micro is the micro version number
    -   it is incremented for each bugfix or security release.

We'll keep major version 3. Python 3 is the brand; there will be no
Python 4.

In the year 2100, the minor will be 2100-2000 = 100, therefore the
version will be 3.100.0.

Python 3.14 will be the last version before this change, released in
2025. Python 3.26 will be the first version after this change, released
in 2026. There will be no Python 3.15 to 3.25 inclusive.

Security implications

None known. No change to durations or timing of bug fix and security
phases.

How to teach this

We will announce this on blogs, in the 3.14 release notes,
documentation, and through outreach to the community.

This change targets the version following 3.14: instead of 3.15 it will
be 3.26. This PEP was proposed in June 2024. Development for the
3.15/3.26 release will begin in May 2025, with the first alpha in
October 2025 and initial release in October 2026. We can already update
documentation during the 3.14 cycle. This gives plenty of notice.

We can make preview builds which only change the version for early
testing.

We could ship a python3.15 command as part of Python 3.26 that
immediately errors out and tells the user to use python3.26 instead.

Rejected ideas

YY.0

For example, Python 26.0 would be released in 2026.

There's not much appetite for Python version 4. We don’t want to repeat
2-to-3, and 4 has a lot of expectations by now. We don’t want
“earth-shattering changes”.

Perhaps Python 4 could be reserved for something big like removing the
GIL (PEP 703), but the Steering Council made it clear the free-threading
rollout must be gradual. Will we stick with version 3 forever?

Another option would be to put the year in the major version and jump to
26.0. This could mean we could leapfrog all that 4.0 baggage.

Platform compatibility tags

Changing the major version would complicate packaging, however.

The packaging:platform-compatibility-tags specification says the Python
version tag used in wheel filenames is given by
sysconfig.get_config_var("py_version_nodot"), where the major and minor
versions are joined together without a dot. For example, 3.9 is 39.

During the 3.10 alpha, there was ambiguity because 310 can be
interpreted as 3.10, 31.0, or 310.

The specification says an underscore can be used if needed, and PEP 641
("Using an underscore in the version portion of Python 3.10
compatibility tags") proposed this:

                         Version → tag → version             PEP 641 proposed version
  ---------------------- ----------------------------------- --------------------------
  Pre-3.10               3.9 → 39                            
  Ambiguity after 3.10   3.10 → 310 → 3.10 or 31.0 or 310?   3_10
  Ambiguity with YY.xx   26.0 → 260 → 2.60 or 26.0 or 260?   26_0

However, PEP 641 was rejected because it was unknown what side effects
there would be on code we’re not aware of.

We would need something like this for YY.0 versioning, which would be a
significant amount of complex work.

Ecosystem changes

Would changing the major version to double digits break code?

Yes, any novel change to the version inevitably does because people make
assumptions, such as the major always being 3, or that the version parts
are always single digits. For example:

+------------+----------------------------------------+-------+-----+
| Version    | Example                                | Exp   | Act |
| change     |                                        | ected | ual |
+============+========================================+=======+=====+
| 2.7.9 →    |     'this                              | 2     | 2.  |
| 2.7.10     |  is Python {}'.format(sys.version[:5]) | .7.10 | 7.1 |
+------------+----------------------------------------+-------+-----+
| 3.9 → 3.10 |     ".%s-%s                            | 3.10  | 3.1 |
|            | " % (get_platform(), sys.version[0:3]) |       |     |
+------------+----------------------------------------+-------+-----+
| 3 → 4      |     if sys.version_info[1] >= 9:       | 4.0   | 0   |
+------------+----------------------------------------+-------+-----+
| 3 → 26     |     if sys.version[0] == '3':          | 26    | 2   |
+------------+----------------------------------------+-------+-----+

The last one here is most relevant for YY.0 versioning. Therefore the
3.YY scheme is the safest and requires fewest changes, because the shape
of the version doesn't change: it's still a 3 followed by two digits.

Tip

Use Ruff's YTT rules or Flake8's flake8-2020 plugin to help find the
problems like these.

python3 command

PEP 394 (The “python” Command on Unix-Like Systems) outlines
recommendations for the python, python2 and python3 commands. python can
map to either python2 or python3. These would need revisiting if the
major version changed, and started changing annually.

Four years after Python 2.7's end-of-life, we could recommend python
only maps to the latest Python 3+ version. But what would python3 map to
when Python 26.0 is out? This would introduce additional complexity and
cost.

CPython changes

In addition to python3 command changes, there are at least four places
in CPython that assume the major version is 3 and would need updating:

-   Lib/ast.py
-   Parser/pegen.c
-   Parser/pegen.h
-   Parser/string_parser.c

YY.0 rejection

The benefits of calendar versioning are not so big compared to the
combined costs for YY.0 versioning. Therefore, YY.0 versioning is
rejected.

YY.MM

For example, Python 26.10 would be released in October 2026.

Building upon YY.0 versioning, we could also include the release month
as the minor version, like Ubuntu and Black. This would make it clear
when in the year it was released, and also when in the year it will
reach end-of-life.

However, YY.MM versioning is rejected for many of the same reasons as
YY.0 versioning.

3.YYYY

For example, Python 3.2026 would be released in 2026.

It's clearer that the minor version is a year when using a four digits,
and avoids confusion with Ubuntu versions which use YY.MM.

PY_VERSION_HEX

CPython's C API PY_VERSION_HEX macro currently uses eight bits to encode
the minor version, accommodating a maximum minor version of 255. To hold
a four-digit year, it would need to be expanded to 11 bits to fit 2047
or rather 12 bits for 4095.

This looks feasible, as it's intended for numeric comparisons, such as
#if PY_VERSION_HEX >= .... In the top 8,000 PyPI projects only one
instance was found of bit shifting
(hexversion >> 16 != PY_VERSION_HEX >> 16).

However, 3.YYYY is rejected as changing from two to four digits would
nevertheless need more work and break more code than simpler 3.YY
versioning.

Editions

For example, Python 3.15 (2026 Edition) would be released in 2026.

The Rust language uses "Editions" to introduce breaking changes.
Applying this to Python would require big changes to PEP 387 (Backwards
Compatibility Policy) and is out of scope for this PEP.

We could apply a year label to releases, such as "Python 3.15 (2026
Edition)", but this is rejected because we'd have to keep track of two
numbers.

Adopt SemVer and skip 4

For example, Python 5.0 would be released in 2026, 6.0 in 2027, and so
on.

We could skip the problematic 4.0 entirely and adopt SemVer. Because
deprecations are removed in every feature release, we would get a new
major bump every year.

This is rejected because we wouldn't get the benefit of calendar
versioning, and moving away from 3.x would also break code.

Change during 3.14 cycle

The Python 3.14 release must go ahead because: π.

Backwards compatibility

This version change is the safest of the CalVer options considered (see
rejected ideas): we keep 3 as the major version, and the minor version
is still two digits. The minor will eventually change to three digits
but this is predictable, a long way off and can be planned for.

We retain the python3 executable.

Version mapping

Versions 3.15 to 3.25 inclusive will be skipped. Features, deprecations
and removals planned for these will be remapped to the new version
numbers.

For example, a deprecation initially planned for removal in 3.16 will
instead be removed in 3.27.

  --------------------------------------------------
  Old version   New version        Initial release
  ------------- ------------------ -----------------
  3.14          3.14 (no change)   2025

  3.15          3.26               2026

  3.16          3.27               2027

  3.17          3.28               2028

  3.18          3.29               2029

  3.19          3.30               2030

  3.20          3.31               2031

  3.21          3.32               2032

  3.22          3.33               2033

  3.23          3.34               2034

  3.24          3.35               2035

  3.25          3.36               2036
  --------------------------------------------------

Forwards compatibility

Future change in cadence

This PEP proposes no change to the annual release cadence as defined in
PEP 602, which lays out
many good reasons for annual releases <602#rationale-and-goals> (for
example, smaller releases with a predictable release calendar, and
syncing with external redistributors). However unlikely, should we
decide to change the cadence in the future, CalVer does not preclude
doing so.

Less frequent

If we went to fewer than one release per year, the proposed CalVer
scheme still works; indeed, it even helps people know in which year to
expect the release. For example, if we released every second year
starting in 2036:

-   3.36.0 would be released in 2036
-   3.38.0 would be released in 2038
-   and so on

Ecosystem changes depend in part on how the the hypothetical
cadence-changing PEP updates PEP 387 (Backwards Compatibility Policy).
If, for example, it requires that the deprecation period must be at
least one feature release and not the current two (to maintain the
minimum two years), CalVer has the benefit over the status quo in
requiring no changes to planned removal versions (other than adjusting
any falling in non-release years).

More frequent

If we went to more than one release per year, here are some options. For
example, if we released in April and October starting in 2036, the next
four releases could be:

  ---------------------------------------------------------------------------------------
  Scheme          Notes                   2036 a      2036 b      2037 a      2037 b
  --------------- ----------------------- ----------- ----------- ----------- -----------
  YY.MM.micro     Year as major, month as 36.04.0     36.10.0     37.04.0     37.10.0
                  minor                                                       

  YY.x.micro      Year as major, serial   36.1.0      36.2.0      37.1.0      37.2.0
                  number as minor                                             

  3.YYMM.micro    Combine year and month  3.3604.0    3.3610.0    3.3704.0    3.3710.0
                  as minor                                                    

  3.YYx.micro     Combine year and serial 3.360.0     3.361.0     3.370.0     3.371.0
                  number as minor                                             

  3.YY.MM.micro   Add an extra month      3.36.04.0   3.36.10.0   3.37.04.0   3.37.10.0
                  segment                                                     

  3.major.micro   No more CalVer:         3.36.0      3.37.0      3.38.0      3.39.0
                  increment minor                                             

                                          3.50.0      3.51.0      3.52.0      3.53.0

                                          3.100.0     3.101.0     3.102.0     3.103.0

  4.major.micro   No more CalVer:         4.0.0       4.1.0       4.2.0       4.3.0
                  increment major                                             

  5.major.micro                           5.0.0       5.1.0       5.2.0       5.3.0
  ---------------------------------------------------------------------------------------

The YY options would require addressing issues around the platform
compatibility tags, the python3 command, and code assuming the version
always begins with 3.

The options keeping major version 3 but changing the minor to three or
four digits would also need to address code assuming the version is
always two digits.

The option adding an extra month segment is the biggest change as code
would need to deal with a four-part version instead of three.

The options dropping CalVer would be the most conservative allowing the
major and minor to be chosen freely.

No more CalVer

Adopting CalVer now does not preclude moving away CalVer in the future,
for example, back to the original scheme, to SemVer or another scheme.
Some options are listed in the table above. If wanting to make it clear
the minor is no longer the year, it can be bumped to a higher round
number (for example, 3.50 or 3.100) or the major version can be bumped
(for example, to 4.0 or 5.0). Additionally, a version epoch could be
considered.

Footnotes

The author proposed calendar versioning at the Python Language Summit
2024; this PEP is a result of discussions there and during PyCon US.

Read the slides and blogpost of the summit talk.

Acknowledgements

Thanks to Seth Michael Larson for the Language Summit Q&A notes and
blogpost, and to everyone who gave feedback at the summit and PyCon US.

Thank you to Łukasz Langa and Alex Waygood for reviewing a draft of this
PEP.

Copyright

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.