Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6fd7a02
bpo-34008: Py_Main and Py_IsInitialized are preinit functions
ncoghlan Jun 30, 2018
90aea92
Add news entry
ncoghlan Jun 30, 2018
6708545
Fix directive markup
ncoghlan Jun 30, 2018
c2e12cc
Reword Py_Main docs
ncoghlan Jun 30, 2018
eb855e1
Replace note with NEWS entry for Py_Main docs move
ncoghlan Jul 4, 2018
68e37d1
Further pre-initialization test improvements
ncoghlan Jul 4, 2018
f0c8c9d
Adjust wording to be more accurate for 3.6 and 3.7
ncoghlan Aug 23, 2019
e652a86
Add missing word
ncoghlan Aug 23, 2019
225bc42
Fix typo
ncoghlan Aug 23, 2019
036f6f3
Update NEWS entry
ncoghlan Aug 23, 2019
24586e5
Merge remote-tracking branch 'origin/master' into bpo-34008-py-main-a…
ncoghlan Aug 25, 2019
8310a1a
Tweak wording of Py_Main and Py_BytesMain docs
ncoghlan Aug 25, 2019
f3d9713
Tweak presentation order of init functions
ncoghlan Aug 25, 2019
e32d458
Further integrate old and new init API docs
ncoghlan Aug 25, 2019
6b50669
Merge in online PR changes, move InitalizeFromConfig to main init sec…
ncoghlan Aug 25, 2019
fd8d763
Merge branch 'master' into bpo-34008-py-main-after-py-initialize
ncoghlan Jun 28, 2020
70e43bd
Merge remote-tracking branch 'origin/main' into bpo-34008-py-main-aft…
ncoghlan Sep 26, 2024
31b00fb
Merge branch 'main' into bpo-34008-py-main-after-py-initialize
ncoghlan Sep 26, 2024
c6838ca
Fix merge error
ncoghlan Sep 26, 2024
634f1a0
Fix up NEWS entries
ncoghlan Sep 26, 2024
2d75186
Actually reference the init-from-config anchor
ncoghlan Sep 26, 2024
54e5391
Eliminate Py_RunMain docs duplication
ncoghlan Sep 26, 2024
9a6fec5
Fix anchor syntax
ncoghlan Sep 26, 2024
e875817
Replace stale reference to Py_Finalize
ncoghlan Sep 27, 2024
e42ebb5
Fix typo
ncoghlan Sep 27, 2024
0a58fcd
Remove trailing whitespace
ncoghlan Oct 7, 2024
b54aa49
Merge branch 'main' into bpo-34008-py-main-after-py-initialize
ncoghlan Oct 7, 2024
c5bb626
Add back Py_Finalize docs (presumably lost in a merge error)
ncoghlan Oct 7, 2024
aad97ee
Apply suggestions from code review
ncoghlan Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ a few functions and the :ref:`global configuration variables

The following functions can be safely called before Python is initialized:

* Functions that initialize the interpreter:

* :c:func:`Py_Initialize`
* :c:func:`Py_InitializeEx`
* :c:func:`Py_Main`

* Configuration functions:

* :c:func:`PyImport_AppendInittab`
Expand All @@ -44,6 +50,7 @@ The following functions can be safely called before Python is initialized:
* :c:func:`Py_GetCopyright`
* :c:func:`Py_GetPlatform`
* :c:func:`Py_GetVersion`
* :c:func:`Py_IsInitialized`

* Utilities:

Expand Down Expand Up @@ -307,6 +314,47 @@ Initializing and finalizing the interpreter
disregards the return value.


.. c:function:: int Py_Main(int argc, wchar_t **argv)

The main program for the standard interpreter, encapsulating a full
:c:func:`Py_Initialize`/:c:func:`Py_Finalize` cycle, as well as additional
behaviour to implement reading configurations settings from the environment
and command line, and then executing ``__main__`` in accordance with
:ref:`using-on-cmdline`.

This is made available for programs which wish to support the full CPython
command line interface, rather than just embedding a Python runtime in a
larger application.

The *argc* and *argv* parameters should be prepared exactly as those which
are passed to a C program's :c:func:`main` function (converted to ``wchar_t``
with :c:func:`Py_DecodeLocale`). It is important to note that the argument
list may be modified (but the contents of the strings pointed to by the
argument list are not).

The return value will be ``0`` if the interpreter exits normally (i.e.,
without an exception), ``1`` if the interpreter exits due to an exception,
or ``2`` if the argument list does not represent a valid Python command
line.

Note that if an otherwise unhandled :exc:`SystemExit` is raised, this
function will not return ``1``, but exit the process, as long as
``Py_InspectFlag`` is not set. If ``Py_InspectFlag`` is set, execution will
drop into interactive Python prompt, at which point a second otherwise
unhandled :exc:`SystemExit` will still exit the process, while any other
means of exiting will set the return value as described above.

In normal usage, an embedding application will call this function
*instead* of calling :c:func:`Py_Initialize` or :c:func:`Py_InitializeEx`
directly, and all settings will be applied as described elsewhere in this
documentation. If this function is instead called *after* a preceding
``Py_Initialize`` or ``Py_InitializeEx`` call, then exactly which
environmental and command line configuration settings will be updated is
version dependent (as it depends on which settings correctly support
being modified after they have already been set once in the first call to
``Py_Initialize`` or ``Py_InitializeEx``).


Process-wide parameters
=======================

Expand Down
17 changes: 0 additions & 17 deletions Doc/c-api/veryhigh.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,6 @@ are only passed to these functions if it is certain that they were created by
the same library that the Python runtime is using.


.. c:function:: int Py_Main(int argc, wchar_t **argv)

The main program for the standard interpreter. This is made available for
programs which embed Python. The *argc* and *argv* parameters should be
prepared exactly as those which are passed to a C program's :c:func:`main`
function (converted to wchar_t according to the user's locale). It is
important to note that the argument list may be modified (but the contents of
the strings pointed to by the argument list are not). The return value will
be ``0`` if the interpreter exits normally (i.e., without an exception),
``1`` if the interpreter exits due to an exception, or ``2`` if the parameter
list does not represent a valid Python command line.

Note that if an otherwise unhandled :exc:`SystemExit` is raised, this
function will not return ``1``, but exit the process, as long as
``Py_InspectFlag`` is not set.


.. c:function:: int PyRun_AnyFile(FILE *fp, const char *filename)

This is a simplified interface to :c:func:`PyRun_AnyFileExFlags` below, leaving
Expand Down
11 changes: 10 additions & 1 deletion Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ def run_repeated_init_and_subinterpreters(self):
# Parse the line from the loop. The first line is the main
# interpreter and the 3 afterward are subinterpreters.
interp = Interp(*match.groups())
if support.verbose > 1:
if support.verbose > 2:
# 5 lines per pass is super-spammy, so limit that to -vvv
print(interp)
self.assertTrue(interp.interp)
self.assertTrue(interp.tstate)
Expand Down Expand Up @@ -195,6 +196,10 @@ def test_pre_initialization_api(self):
"""
env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
out, err = self.run_embedded_interpreter("pre_initialization_api", env=env)
if support.verbose > 1:
print()
print(out)
print(err)
if sys.platform == "win32":
expected_path = self.test_exe
else:
Expand All @@ -211,6 +216,10 @@ def test_pre_initialization_sys_options(self):
env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
out, err = self.run_embedded_interpreter(
"pre_initialization_sys_options", env=env)
if support.verbose > 1:
print()
print(out)
print(err)
expected_output = (
"sys.warnoptions: ['once', 'module', 'default']\n"
"sys._xoptions: {'not_an_option': '1', 'also_not_an_option': '2'}\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Make it explicit that we expect ``Py_Main`` to typically be called instead
of ``Py_Initialize`` rather than after it (since ``Py_Main`` makes its own
call to ``Py_Initialize``). However, also document that calling both is
supported, but it's version dependent as to exactly which settings
will be applied correctly in that case.

Also add Py_IsInitialized to the list of APIs that are safe to call before
the interpreter is initialized, and update the embedding tests to cover it.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The Py_Main documentation moved from the Very High Level API section to the
Initialization and Finalization section.
41 changes: 33 additions & 8 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,36 @@ static int test_pre_initialization_api(void)
_Py_EMBED_PREINIT_CHECK("Checking Py_SetProgramName\n");
Py_SetProgramName(program);

_Py_EMBED_PREINIT_CHECK("Checking !Py_IsInitialized pre-initialization\n");
if (Py_IsInitialized()) {
fprintf(stderr, "Fatal error: initialized before initialization!\n");
return 1;
}

_Py_EMBED_PREINIT_CHECK("Initializing interpreter\n");
Py_Initialize();

_Py_EMBED_PREINIT_CHECK("Checking Py_IsInitialized post-initialization\n");
if (!Py_IsInitialized()) {
fprintf(stderr, "Fatal error: not initialized after initialization!\n");
return 1;
}

_Py_EMBED_PREINIT_CHECK("Check sys module contents\n");
PyRun_SimpleString("import sys; "
"print('sys.executable:', sys.executable)");
PyRun_SimpleString(
"import sys; "
"print('sys.executable:', sys.executable); "
"sys.stdout.flush(); "
);
_Py_EMBED_PREINIT_CHECK("Finalizing interpreter\n");
Py_Finalize();

_Py_EMBED_PREINIT_CHECK("Checking !Py_IsInitialized post-finalization\n");
if (Py_IsInitialized()) {
fprintf(stderr, "Fatal error: still initialized after finalization!\n");
return 1;
}

_Py_EMBED_PREINIT_CHECK("Freeing memory allocated by Py_DecodeLocale\n");
PyMem_RawFree(program);
return 0;
Expand Down Expand Up @@ -202,12 +224,15 @@ static int test_pre_initialization_sys_options(void)
_Py_EMBED_PREINIT_CHECK("Initializing interpreter\n");
_testembed_Py_Initialize();
_Py_EMBED_PREINIT_CHECK("Check sys module contents\n");
PyRun_SimpleString("import sys; "
"print('sys.warnoptions:', sys.warnoptions); "
"print('sys._xoptions:', sys._xoptions); "
"warnings = sys.modules['warnings']; "
"latest_filters = [f[0] for f in warnings.filters[:3]]; "
"print('warnings.filters[:3]:', latest_filters)");
PyRun_SimpleString(
"import sys; "
"print('sys.warnoptions:', sys.warnoptions); "
"print('sys._xoptions:', sys._xoptions); "
"warnings = sys.modules['warnings']; "
"latest_filters = [f[0] for f in warnings.filters[:3]]; "
"print('warnings.filters[:3]:', latest_filters); "
"sys.stdout.flush(); "
);
_Py_EMBED_PREINIT_CHECK("Finalizing interpreter\n");
Py_Finalize();

Expand Down