Building from source: where does the version string come from?

I noticed with my versions of Python that I compiled from source locally (all from downloaded and extracted tarballs):

$ Python-3.8.19/python -c "import sys; print(sys.version)"
3.8.19 (default, Jun 24 2024, 07:57:42) 
[GCC 11.4.0]
$ Python-3.9.19/python -c "import sys; print(sys.version)"
3.9.19 (main, Jun 24 2024, 07:54:31) 
[GCC 11.4.0]
$ Python-3.10.14/python -c "import sys; print(sys.version)"
3.10.14 (main, Jun 24 2024, 03:37:47) [GCC 11.4.0]

In 3.9, default changes to main, and in 3.10 the newline disappears.

I guess that main is supposed to be the name of the Git branch exported to make the tarball, but I can’t recall that name having been “default” before.

How does this string get created, in the first place? (It’s not like I can just check the source, since sys is implemented in C, is thousands of lines long, and is pulling in other stuff via the C preprocessor rather than a proper import mechanism.)


Side note, since I recompiled everything today: my Python 3.5 (specifically! Before you ask, I deliberately build old versions so I can do things like this) build gave me multiple errors like

/path/to/Python-3.5.10/Modules/nismodule.c:17:10: fatal error: rpc/rpc.h: No such file or directory
   17 | #include <rpc/rpc.h>
      |          ^~~~~~~~~~~
compilation terminated.

But even so, a Python 3.5 executable is produced with an up-to-date timestamp, and I can run it and import nis without issue. How is this possible?

default was the name of the branch in the Mercurial (hg) repository that existed before the migration to GitHub.

Start from the Py_GetVersion function and follow the breadcrumbs :slight_smile:. The right sidebar in the GitHub interface should guide you along fairly reasonably.

Seems likely that it’s picking up another nis module somewhere. It’s generally not built in, so if it’s found somewhere in sys.path it will be loaded from there.

Ofcourse you can read sysmodule.c. A quick search for "version" (with quotes) leads to this line, which leads to this file:

From here the github navigation is pretty good at finding all the definitions.

When I ctrl-F’d on sysmodule.c there were hundreds of results, so I gave up rather easily. I personally try to keep my source files (in any language) to less than 500 lines (actually more like 200 when it’s reasonably easy)…

Oh, that’s strange. The compiled Python in the project folder doesn’t have the module. But the installed one - oh, right, I must have overwritten the old install and the old version of the .so would be in the same place.

Wish I understood the failure, though. It doesn’t seem to affect other versions, and I should have the necessary system dependency, since I followed the dev guide.

Which is why I searched more targeted: I knew it would end up as an attribute name, so it must either be alone as a string "version" or _Py_ID(version) [1]. The first I tried already only gave me two results, one of which was the relevant one.


  1. Other forms might also be possible, but seem very unlikely to me. ↩︎

1 Like