GeezerVison is showing github.com — C API: copying a PyStatus emits -Wdeprecated-declarations in C++ with clang since gh-158080 · Issue #160436 · python/cpython
C API: copying a PyStatus emits -Wdeprecated-declarations in C++ with clang since gh-158080 #160436DescriptionBug description:
Since gh-158080 (GH-158085), PyStatus.exitcode is declared with Py_DEPRECATED(3.16) in Include/cpython/initconfig.h. Because the C API passes and returns PyStatus by value (PyStatus_Exception(), PyStatus_IsError(), Py_InitializeFromConfig(), ...), C++ code that uses the API in the documented way makes clang generate the implicit copy constructor / move assignment operator of PyStatus. These use the deprecated member, so clang emits -Wdeprecated-declarations even though the code never touches exitcode. With -Werror, the build fails.
The usage pattern of the Doc/c-api/init_config.rst examples (ex: init_python()), compiled as C++, is enough to reproduce:
#include <Python.h>
int main(int argc, char** argv) {
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
if (PyStatus_Exception(status)) {
goto exception;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
return Py_RunMain();
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
$ clang++ -std=c++17 -Wall -Werror -I<cpython>/Include -I<build> -c embed.cpp
Include/cpython/initconfig.h:10:9: error: 'exitcode' is deprecated [-Werror,-Wdeprecated-declarations]
embed.cpp:7:12: note: in implicit move assignment operator for 'PyStatus' first required here
Include/cpython/initconfig.h:10:9: error: 'exitcode' is deprecated [-Werror,-Wdeprecated-declarations]
Include/cpython/initconfig.h:10:9: error: 'exitcode' is deprecated [-Werror,-Wdeprecated-declarations]
embed.cpp:8:28: note: in implicit copy constructor for 'PyStatus' first required here
- Reproduced with 3.16.0a1, with Ubuntu clang 21.1.8 (Linux aarch64) and Apple clang 21.0.0 (macOS arm64); the
PyStatus definition is unchanged on current main.
- g++ 15, and clang compiling the same file as C, emit no warning.
- The diagnostic is reported at the definition of the structure in
initconfig.h, so users cannot silence it locally with a pragma around their own calls; they have to wrap the #include <Python.h> itself.
Possible fix
Suppress the warning around the definition of the structure, with the existing _Py_COMP_DIAG_* macros of pyport.h:
/* --- PyStatus ----------------------------------------------- */
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
typedef struct {
enum {
_PyStatus_TYPE_OK=0,
_PyStatus_TYPE_ERROR=1,
_PyStatus_TYPE_EXIT=2 // deprecated
} _type;
const char *func;
const char *err_msg;
Py_DEPRECATED(3.16) int exitcode;
} PyStatus;
+_Py_COMP_DIAG_POP
With this change the example above compiles cleanly with clang++ -Werror, while real uses (status.exitcode, PyStatus_Exit(), PyStatus_IsExit()) still emit the deprecation warning. (Alternatively, only the functions could be marked deprecated and the member documented as deprecated.)
A C++ test in Lib/test/test_cppext that copies a PyStatus would catch regressions of this kind.
CPython versions tested on:
3.16, CPython main branch
Operating systems tested on:
Linux, macOS
Linked PRs
ActivityNekoAsakura commented on Oct 27, 2026 Reproduced with clang 5 to 21. It looks like we already do the pragma option for deprecated code elsewhere.
|
_Py_COMP_DIAG_PUSH |
|
_Py_COMP_DIAG_IGNORE_DEPR_DECLS |
|
#ifdef Py_GIL_DISABLED |
|
return _Py_atomic_load_ssize_relaxed(&self->ob_shash); |
|
#else |
|
return self->ob_shash; |
|
#endif |
|
_Py_COMP_DIAG_POP |
@vstinner shall I go with that? vstinner commented on Oct 27, 2026 I cannot reproduce the C++ warning on Fedora 44 with g++ 16.2.1 or clang++ 22.1.8 using your example.
Building this C++ code with g++ -std=c++17 x.cpp -o -x -Wall -Wextra or clang++ -std=c++17 x.cpp -o -x -Wall -Wextra doesn't emit compiler warnings neither:
struct PyStatus {
int status;
__attribute__((__deprecated__)) int exitcode;
};
PyStatus func(void)
{
#if defined(__clang__)
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
#else
_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#endif
PyStatus status = {1, 0};
#if defined(__clang__)
_Pragma("clang diagnostic pop")
#else
_Pragma("GCC diagnostic pop")
#endif
return status;
}
int main(void)
{
PyStatus copy = func();
return copy.status == 0;
}vstinner commented on Oct 27, 2026 Can you please try this patch?
diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h
index 846e05fb26a..1dcb7807761 100644
--- a/Include/cpython/initconfig.h
+++ b/Include/cpython/initconfig.h
@@ -15,7 +15,13 @@ typedef struct {
} _type;
const char *func;
const char *err_msg;
+#ifndef __cplusplus
Py_DEPRECATED(3.16) int exitcode;
+#else
+ // Avoid Py_DEPRECATED() on C++ to avoid a compile warning on an (implicit)
+ // PyStatus move assignment operator.
+ int exitcode;
+#endif
} PyStatus;
PyAPI_FUNC(PyStatus) PyStatus_Ok(void);vstinner commented on Oct 27, 2026 @NekoAsakura: _Py_COMP_DIAG_IGNORE_DEPR_DECLS is designed to ignore a deprecation warning when using a deprecated function/API. I don't think that it should be used when marking an API as deprecated. NekoAsakura commented on Oct 28, 2026
I cannot reproduce the C++ warning on Fedora 44 with g++ 16.2.1 or clang++ 22.1.8 using your example.
@vstinner That's because it was fixed in clang >= 22 (#147400). I can confirm your example also warns on older versions.
I also tried your patch. It fixes the issue, but it causes a regression where using status.exitcode in C++ no longer warns.
_Py_COMP_DIAG_IGNORE_DEPR_DECLS is designed to ignore a deprecation warning when using a deprecated function/API. I don't think that it should be used when marking an API as deprecated.
It is usually used around code that uses a deprecated API, but here the warning is reported inside Python.h, so users can't silence it in their own code. davidnich commented on Oct 28, 2026 Tested with Apple clang 21.0.0 (clang-2100.3.34.2, current Xcode, arm64), using a copy of the 3.14 headers with PyStatus.exitcode declared as in 3.16, with -std=c++17 -Wall:
initconfig.h |
example from the report |
s.exitcode in C++ |
s.exitcode in C |
| current (3.16) |
3 warnings |
warning |
warning |
#ifndef __cplusplus patch |
no warning |
no warning |
warning |
_Py_COMP_DIAG_PUSH / _Py_COMP_DIAG_IGNORE_DEPR_DECLS around the struct definition |
no warning |
warning |
warning |
So, as @NekoAsakura says, the #ifndef __cplusplus patch fixes the false positive but drops the deprecation for real C++ uses. Suppressing the warning only around the definition keeps it. Clang reports the warning for a use in the user's own code, outside the push/pop region, so only the implicit copy/move members generated from the definition are affected. Apple's current clang 21 still has the behavior fixed in clang 22 (llvm/llvm-project#147400), so macOS embedders building with Xcode will hit this until Xcode ships clang 22+. vstinner commented on Nov 4, 2026
@vstinner That's because it was fixed in clang >= 22 (llvm/llvm-project#147400). I can confirm your example also warns on older versions.
Ah. In that case, I suggest to skip Py_DEPRECATED() on clang++ 21 and older. It's not a big deal, PyStatus.exitcode deprecation is not very important :-)
@NekoAsakura: Do you want to propose a PR for that?
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|