Skip to content

Commit 9eadefd

Browse files
friedfacebook-github-bot
authored andcommitted
Weak.h a solution to everyone linking libpython for code inspection in c++
Summary: We have this issue there are some core C++ libraries that dep on libpython, causing C++ binaries to have dependencies on python when the service owners are unaware why their C++ service now needs libpython3.12.so when to their knowledge they don't use python at all. The goal of this project is to provide weak symbols and a unified way to tell if python is linked or not. That way the core c++ libraries need not themselves link libpython, but if it is linked they can use it in limited ways. see: S504477 and https://fb.workplace.com/groups/pyupgrades/posts/3601313680172758/ for justification Reviewed By: parulgupta1004 Differential Revision: D72285611 fbshipit-source-id: 4d0820f2d37e1c65eb6e415d1c7aa01d901e1f8e
1 parent d967b59 commit 9eadefd

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

folly/python/Weak.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <Python.h> // @manual=fbsource//third-party/python:python-headers
20+
21+
extern "C" {
22+
23+
// NOT_WINDOWS
24+
#if defined(__APPLE__) || defined(__linux__)
25+
26+
#if defined(__APPLE__)
27+
#define Py_Weak(RTYPE) __attribute__((weak_import)) extern RTYPE
28+
#else
29+
#define Py_Weak(RTYPE) __attribute__((weak)) extern RTYPE
30+
#endif
31+
32+
// These symbols provides our base for detecting python is loaded
33+
// See folly::python::isLinked()
34+
Py_Weak(void) Py_IncRef(PyObject*);
35+
Py_Weak(void) Py_DecRef(PyObject*);
36+
Py_Weak(const char*) Py_GetVersion(void);
37+
38+
// Modules
39+
Py_Weak(PyObject*) PyImport_ImportModule(const char*);
40+
41+
// Exception Handling
42+
Py_Weak(PyObject*) PyErr_Occurred(void);
43+
Py_Weak(void) PyErr_Clear(void);
44+
Py_Weak(void) PyErr_Fetch(PyObject**, PyObject**, PyObject**);
45+
46+
// Object Handling
47+
Py_Weak(PyObject*) PyObject_Repr(PyObject*);
48+
49+
// Unicode && Bytes Handling
50+
Py_Weak(char*) PyBytes_AsString(PyObject*);
51+
Py_Weak(PyObject*)
52+
PyUnicode_AsEncodedString(PyObject*, const char*, const char*);
53+
Py_Weak(const char*) PyUnicode_AsUTF8(PyObject*);
54+
55+
// Basic GIL Handling
56+
Py_Weak(PyThreadState*) PyGILState_GetThisThreadState(void);
57+
Py_Weak(int) PyGILState_Check(void);
58+
Py_Weak(PyGILState_STATE) PyGILState_Ensure(void);
59+
Py_Weak(void) PyGILState_Release(PyGILState_STATE);
60+
61+
// Some Frame and Traceback Handling
62+
Py_Weak(PyFrameObject*) PyThreadState_GetFrame(PyThreadState*);
63+
Py_Weak(int) PyFrame_GetLineNumber(PyFrameObject*);
64+
Py_Weak(void) _Py_DumpTraceback(int, PyThreadState*);
65+
Py_Weak(PyCodeObject*) PyFrame_GetCode(PyFrameObject*);
66+
Py_Weak(PyFrameObject*) PyFrame_GetBack(PyFrameObject*);
67+
#if PY_VERSION_HEX >= 0x030b0000 // >= 3.11
68+
Py_Weak(int) PyFrame_GetLasti(PyFrameObject*);
69+
#endif
70+
71+
// Runtime State
72+
Py_Weak(int) Py_IsInitialized(void);
73+
#if PY_VERSION_HEX >= 0x030d0000 // >= 3.13
74+
Py_Weak(int) Py_IsFinalizing(void);
75+
#else
76+
Py_Weak(int) _Py_IsFinalizing(void);
77+
#endif
78+
79+
#undef Py_Weak
80+
#endif // NOT_WINDOWS
81+
82+
} // extern "C"
83+
84+
// So windows can use these helpers
85+
#if PY_VERSION_HEX < 0x030b0000 // < 3.11
86+
#include <frameobject.h>
87+
inline int PyFrame_GetLasti(PyFrameObject* frame) {
88+
return frame->f_lasti;
89+
}
90+
#endif
91+
92+
#if PY_VERSION_HEX < 0x030d0000 // < 3.13
93+
inline int Py_IsFinalizing() {
94+
return _Py_IsFinalizing();
95+
}
96+
#endif
97+
98+
namespace folly::python {
99+
100+
// Lets use these symbols if they are defined we can assume python is loaded
101+
inline bool isLinked() {
102+
#if defined(__APPLE__) || defined(__linux__)
103+
return (Py_IncRef != nullptr) && (Py_DecRef != nullptr) &&
104+
(Py_GetVersion != nullptr);
105+
#else
106+
return true;
107+
#endif
108+
}
109+
110+
} // namespace folly::python

folly/python/test/WeakTest.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <gtest/gtest.h>
18+
#include <folly/python/Weak.h>
19+
20+
TEST(WeakPython, isLinked) {
21+
if (folly::python::isLinked()) {
22+
EXPECT_TRUE(Py_GetVersion != nullptr);
23+
auto version = Py_GetVersion();
24+
EXPECT_NE(version, nullptr);
25+
} else {
26+
EXPECT_TRUE(Py_GetVersion == nullptr);
27+
}
28+
}
29+
30+
TEST(WeakPython, isFinalizing) {
31+
// This should always be false, since we never finalize an interperter
32+
// And never SEGFAULT
33+
EXPECT_EQ(false, folly::python::isLinked() && Py_IsFinalizing());
34+
}

0 commit comments

Comments
 (0)