In this post, we will take a dissecion of source code of Python.
To benefit the simplicity and meanwhile follow the most recent functionnalities, I choose Python 3.6.9
to do the analysis.
In the last post, the entry of Python is found, so it’s time to look at it!
Py_Main - the Python Main function
According to C89 standard, in a C function, all the variables should be declared at the beginning. So, we can take a look at those variables.
Variables
The variables are listed below, though we’ll not explicite them at the moment.
1 | int c; |
Arguments
Then, after listing and initializing all variables, Python will try to find flags in the arguments.
First, Python tries to find some options which are needed by some initializations.
1 | /* Hash randomization needed early for all string operations |
PROGRAM_OPTS
is defined as BASE_OPTS
, and #define BASE_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?"
is at the header of main.c
.
_PyOS_GetOpt
is implemented in Python/getopt.c
, which validates and returns argument option. If an option is not in PROGRAM_OPTS
, a _
will be returned. It will not accept --argument
and returns a -1 if an argument with that form is found.
In these lines, only options E
, m
, c
are detected:
- if
E
is detected, the flag which leads to the negligence ofPYTHONPATH
,PYTHONHOME
environment variables. - once
m
orc
is detected, following parameters should be the name of module(form
option) or the command that will be executed(forc
option). So we terminated this loop.
Then, Python gets the PYTHONMALLOC
variables and tries to use it to setup allocators.
1 | opt = Py_GETENV("PYTHONMALLOC"); |
Valid allocators are pymalloc
, pymalloc_debug
, malloc
, malloc_debug
and debug
. If you’d like to get more about them, Objects/obmalloc.c
is a good place.
And then Python does an initialization of Random module. In this module, PYTHONHASHSEED
can be used to initialize random module. And it resets warning options, resets option parsing process to process all options.
1 | _PyRandom_Init(); |
We finally enter the period to parse all arguments. They will be explicited in order.
Option c
-c cmd : program passed in as string (terminates option list)`
1 | if (c == 'c') { |
If we encounter an c
option, all other arguments will be neglected. The following argument will be parsed as the commands to be run.
Option m
-m mod : run library module as a script (terminates option list)
1 | if (c == 'm') { |
If we encounter an m
option, all other arguments will be neglected. The following argument will be parsed as the module to be run.
Other options
1 | -B : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x |
1 | switch (c) { |
Actions and behaviors
After recording all options in specified flags, Python can handle them with a determined order or a determined prority (if actions are incompatible).
Help, Version
1 | if (help) |
The option for help has the highest priority, then the option for the version. If they occured, Python will directly terminate after executing their proper actions.
Sync with env
Then, Python tries to get env
1 | if (!Py_InspectFlag && |
One thing should be noticed is that, Py_GETENV
is a macro like this:
1 |
which will actually return NULL if the flag Py_IgnoreEnvironmentFlag
is not zero. The flag is set by -E
option. I think this is a good design.
Parse warning option
Python then uses the code below to parse warning option in different systems, since they might have different default character sets.
1 |
|
At the end, add them to Python warning option list.
Get script file name
Get filename if no command, no module, the arguments are not all read, and the current argument is not -
.
1 | if (command == NULL && module == NULL && _PyOS_optind < argc && |
Check interactivity of current terminal
1 | stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0); |
So, stdin is always interactive.
Play with buffers
Use -u
option to disable input/ouput buffer in Python. This can resolve some problem if you’d like to use pipe as the input or the output of a Python program.
1 | if (Py_UnbufferedStdioFlag) { |
If Py_UnbufferedStdioFlag
is not set, but we’ll enter the interactive mode, do not either use the input/output buffer.
1 | else if (Py_InteractiveFlag) { |
Play with program name
Python wants to set itself as the program name through Py_SetProgramName
function. It’s a simple function, but on macOS, the Python interpreter can be in an App package rather than a bare environment. So, it requires lots of lines to retrieve the program name.
1 |
|
Otherwise, we can see that, argv[0]
is passed in.
Initialize Python
This function will call Py_InitializeEx(1);
and then _Py_InitializeEx_Private(install_sigs, 1);
.
1 | Py_Initialize(); |
The function _Py_InitializeEx_Private
will establish the entire environment. In the next post, we can get deeper in it.
Print Python version in interactive mode
Python will then print Python version if we want to directly enter into interactive mode, i.e, run python
without any other arguments.
1 | if (!Py_QuietFlag && (Py_VerboseFlag || |
Prepare argv for Python os.argv
Use -m
or -c
as premier argument.
1 | if (command != NULL) { |
Prepare main importer path
Main importer path is exactly the module in which Python will run as __main__
. So, to launch Python with a file in the arguments, it’s to use the file as the main importer path.
1 | if (filename != NULL) { |
If there is no file name provided, main_importer_path
will not be set, either. So we do not know which we should use as the first parameter in sys.argv
. Python chooses to treat it after.
1 | if (main_importer_path != NULL) { |
Prepare for interactive mode
If we are going to use interactive/inspect mode, we’ll need the library readline
. So, import it and decrease the reference count of it, to delete it later.
1 | if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) && |
Run command if conform
1 | if (command) { |
Run module if conform
1 | else if (module) { |
Run interactive or run files
If neither command nor module conforms, Python will try to find other way to launch.
Firstly, it tries to run interactive mode, while filename is not set.
This action is what happens in the background when we launch python
without arguments from our terminal.
But it is not where the program begins. Here, we’ve just launched a hook to mark that we need interactive mode.
1 | if (filename == NULL && stdin_is_interactive) { |
Then, if main_importer_path
is set(previously by the file), the main program will run with the file as the main importer. As you know, import all things in the module and set __name__
to __main__
.
1 | sts = -1; /* keep track of whether we've already run __main__ */ |
But what will happen if file name is set, but main importer is not found, or the file doesn’t exist?
Python tries to open the file and read it line by line, then executes it.
1 | if (sts==-1 && filename != NULL) { |
sts
will be set to another value, so the following lines will only be executed if there is no file name given.
1 | if (sts == -1) |
From here, the Python main program is over. Except some small things will be executed after the running.
Re-run Interactive
1 | if (Py_InspectFlag && stdin_is_interactive && |
As we see above, we need Py_InspectFlag
, stdin_is_interactive
are both true, and set at least a file name, a command or a module to run interactive mode. Which means, probably there was one script which has been executed, an the script imported the interactive mode.
1 | /* Check this environment variable at the end, to give programs the |
Thus, run python -c "import os; os.environ['PYTHONINSPECT']='1'"
can also help enter interactive mode 😃
But notice that, it will be executed if Py_InspectFlag
is not set. That means previously we are not in the interactive mode. Thus, run python
and "import os; os.environ['PYTHONINSPECT']='1'"
in it, then exit, this will not help to re-enter the interactive mode.
Clear and end up
1 | if (Py_FinalizeEx() < 0) { |
Conclusion
Up to now, a Python interpreter/program has been launched and finished.
In the next post, we’ll look deeper into _Py_InitializeEx_Private
, to see how Python Main function builds Python env through this function. But at the beginning, we’ll talk about other functions in Python/main.c
, they are small functions but really vital.
See you then!