Skip to content

Commit f7b469e

Browse files
committed
feat: add support for submodules (including recursive submodules) in pybind_extensions
Also changes approach in import shim from regexporting all non-private symbols under new namespace to direct inserting of the original modules under proper names in runtime. Also add some extra testing, such as relative import testing.
1 parent 7a1f315 commit f7b469e

12 files changed

Lines changed: 118 additions & 31 deletions

File tree

pybind/BUILD

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,6 @@ cc_test(
132132
pybind_extension(
133133
name = "pybind",
134134
srcs = ["pybind.cc"],
135-
additional_exported_symbols = [
136-
"_EXTRA_SYMBOL",
137-
],
138135
common_lib_packages = [
139136
"",
140137
"pywrap_external/python", # non-existent package to test that its Ok
@@ -154,9 +151,6 @@ pybind_extension(
154151
pybind_extension(
155152
name = "pybind_copy",
156153
srcs = ["pybind_copy.cc"],
157-
additional_exported_symbols = [
158-
"_EXTRA_SYMBOL",
159-
],
160154
common_lib_packages = [
161155
"",
162156
"pywrap_external",
@@ -253,7 +247,7 @@ pywrap_library(
253247
"//conditions:default": ":linux_framework.lds",
254248
}),
255249
},
256-
pywrap_count = 3,
250+
pywrap_count = 4,
257251
pywrap_lib_filter = ":third_library",
258252
starlark_only_pywrap_count = 1,
259253
visibility = ["//visibility:public"],
@@ -263,6 +257,7 @@ pywrap_library(
263257
":py_library_3",
264258
":py_library_4",
265259
":pybind_with_starlark_only",
260+
"//pybind/sub_pybind:relative_import_lib",
266261
],
267262
)
268263

@@ -291,7 +286,9 @@ py_test(
291286
# not needed, since duplicated, but passed to check that duplication is
292287
# ok
293288
"py_library_1",
289+
"//pybind/sub_pybind:relative_import_lib",
294290
],
291+
# legacy_create_init = False,
295292
)
296293

297294
py_test(

pybind/pybind.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
#include "pybind11/pybind11.h"
55
#include "fifth_library.h"
66

7+
int sub_sub_func(int x) {
8+
return x << 1;
9+
}
10+
711
PYBIND11_MODULE(pybind, m) {
812
fifth_func(); // needed to make sure the dynamic library is loaded on Windows
913
m.doc() = "pybind11 example plugin";
@@ -12,4 +16,10 @@ PYBIND11_MODULE(pybind, m) {
1216
m.def("third_func", &third_func, "The third function");
1317
m.def("second_global_func", &second_global_func, "The second_global function");
1418
m.attr("_EXTRA_SYMBOL") = pybind11::int_(123);
19+
20+
auto subM = m.def_submodule("sub", "submodule");
21+
subM.def("second_func", &second_func, "");
22+
auto subSubM = subM.def_submodule("sub_sub", "sub submodule");
23+
subSubM.def("sub_sub_func", &sub_sub_func, "");
24+
subSubM.def("_sub_sub_private_func", &sub_sub_func, "private sub func");
1525
}

pybind/pybind_py_test.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
import pybind.pybind_copy
77
from pybind.pybind import _EXTRA_SYMBOL as REGULAR_EXTRA_SYMBOL
88
from pybind.pybind_copy import _EXTRA_SYMBOL as REGULAR_COPY_EXTRA_SYMBOL
9-
9+
from pybind.pybind.sub import second_func as sub_second_func
10+
import pybind.pybind.sub
11+
from pybind.pybind.sub.sub_sub import *
12+
from pybind.pybind.sub.sub_sub import _sub_sub_private_func
13+
from pybind.sub_pybind.relative_import_lib import call_nested_pyind_func
14+
from pybind.sub_pybind.relative_import_lib import sub_sub_private_func
1015

1116
class PybindTest(unittest.TestCase):
1217
def _read_file(self, filename, mode="r"):
@@ -48,6 +53,15 @@ def test_pybind_first(self):
4853
self.assertEqual(self._read_file("pybind/static_resource.txt"),
4954
"A static resource file under pybind dir")
5055

56+
print("14: Submodules")
57+
self.assertEqual(sub_second_func(1), 5)
58+
self.assertEqual(pybind.pybind.sub.second_func(1), 6)
59+
self.assertEqual(pybind.pybind.sub.sub_sub.sub_sub_func(3), 6)
60+
self.assertEqual(_sub_sub_private_func(5), 10)
61+
62+
print("15: Nested pybinds and relative imports")
63+
self.assertEqual(call_nested_pyind_func(6), 3)
64+
self.assertEqual(sub_sub_private_func(5), 10)
5165

5266
if __name__ == '__main__':
5367
unittest.main()

pybind/sub_pybind/BUILD

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
load("//rules_pywrap:pywrap.bzl", "pybind_extension")
2+
3+
pybind_extension(
4+
name = "nested_pybind",
5+
srcs = ["nested_pybind.cc"],
6+
common_lib_packages = [
7+
"",
8+
"pywrap_external/python", # non-existent package to test that its Ok
9+
"pybind",
10+
"pybind_external",
11+
"pywrap_external",
12+
],
13+
# visibility = ["//visibility:public"],
14+
deps = [
15+
"//pybind:fifth_library",
16+
],
17+
)
18+
19+
py_library(
20+
name = "relative_import_lib",
21+
srcs = ["relative_import_lib.py"],
22+
deps = [
23+
":nested_pybind",
24+
],
25+
visibility = ["//visibility:public"]
26+
)

pybind/sub_pybind/nested_pybind.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "pybind11/pybind11.h"
2+
#include "pybind/fifth_library.h"
3+
4+
int nested_pybind_func(int x) {
5+
return x >> 1;
6+
}
7+
8+
PYBIND11_MODULE(nested_pybind, m) {
9+
fifth_func(); // needed to make sure the dynamic library is loaded on Windows
10+
m.def("nested_pybind_func", &nested_pybind_func, "");
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from .nested_pybind import nested_pybind_func
2+
from ..pybind.sub.sub_sub import _sub_sub_private_func
3+
4+
def call_nested_pyind_func(x):
5+
return nested_pybind_func(x)
6+
7+
def sub_sub_private_func(x):
8+
return _sub_sub_private_func(x)

pybind_external/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pywrap_library(
88
"//pybind",
99
"//pybind:pybind_cc_only",
1010
"//pybind:pybind_copy",
11+
"//pybind/sub_pybind:relative_import_lib",
1112
],
1213
)
1314

@@ -22,6 +23,7 @@ py_test(
2223
visibility = ["//:__pkg__"],
2324
deps = [
2425
":pybind_external_aggregated",
26+
"//pybind/sub_pybind:relative_import_lib",
2527
],
2628
)
2729

@@ -32,6 +34,7 @@ py_test(
3234
visibility = ["//:__pkg__"],
3335
deps = [
3436
"//pywrap_external:pywrap_external_aggregated",
37+
"//pybind/sub_pybind:relative_import_lib",
3538
],
3639
)
3740

pybind_external/pybind_external_py_test.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
import pybind.pybind_copy
77
from pybind.pybind import _EXTRA_SYMBOL as REGULAR_EXTRA_SYMBOL
88
from pybind.pybind_copy import _EXTRA_SYMBOL as REGULAR_COPY_EXTRA_SYMBOL
9-
9+
from pybind.pybind.sub import second_func as sub_second_func
10+
import pybind.pybind.sub
11+
from pybind.pybind.sub.sub_sub import *
12+
from pybind.pybind.sub.sub_sub import _sub_sub_private_func
13+
from pybind.sub_pybind.relative_import_lib import call_nested_pyind_func
14+
from pybind.sub_pybind.relative_import_lib import sub_sub_private_func
1015

1116
class PybindTest(unittest.TestCase):
1217
def _read_file(self, filename, mode="r"):
@@ -48,6 +53,15 @@ def test_pybind_first(self):
4853
self.assertEqual(self._read_file("pybind/static_resource.txt"),
4954
"A static resource file under pybind dir")
5055

56+
print("14: Submodules")
57+
self.assertEqual(sub_second_func(1), 5)
58+
self.assertEqual(pybind.pybind.sub.second_func(1), 6)
59+
self.assertEqual(pybind.pybind.sub.sub_sub.sub_sub_func(3), 6)
60+
self.assertEqual(_sub_sub_private_func(5), 10)
61+
62+
print("15: Nested pybinds and relative imports")
63+
self.assertEqual(call_nested_pyind_func(6), 3)
64+
self.assertEqual(sub_sub_private_func(5), 10)
5165

5266
if __name__ == '__main__':
5367
unittest.main()

pybind_external/pywrap_external_aggregated_binaries_test.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ def test_pywrap_binaries(self):
3131
(
3232
"/pywrap_external/{lib}pywrap_external_aggregated__starlark_only_common.{extension}",
3333
""),
34+
('/pybind/sub_pybind/nested_pybind.py', ''),
35+
('/pywrap_external/nested_pybind.{pyextension}',
36+
'/pybind/sub_pybind/nested_pybind.{pyextension}'),
3437
]
3538

3639
pyextension = "so"
@@ -40,17 +43,22 @@ def test_pywrap_binaries(self):
4043
if "Windows" in system:
4144
relative_wheel_locations.extend([
4245
("/pywrap_external/framework.2.dll", "/pybind/framework.2.dll"),
43-
("/pywrap_external/framework.2.dll.if.lib", "/pybind/framework.2.dll.if.lib"),
44-
("/pywrap_external/pywrap_external_aggregated__starlark_only_common.dll.if.lib", ""),
45-
("/pywrap_external/pywrap_external_aggregated_common.dll.if.lib", "/pywrap_external/pywrap_external_aggregated_common.dll.if.lib"),
46+
("/pywrap_external/framework.2.dll.if.lib",
47+
"/pybind/framework.2.dll.if.lib"),
48+
(
49+
"/pywrap_external/pywrap_external_aggregated__starlark_only_common.dll.if.lib",
50+
""),
51+
("/pywrap_external/pywrap_external_aggregated_common.dll.if.lib",
52+
"/pywrap_external/pywrap_external_aggregated_common.dll.if.lib"),
4653
])
4754
pyextension = "pyd"
4855
extension = "dll"
4956
lib_prefix = ""
5057
elif "Darwin" in system:
5158
extension = "dylib"
5259
relative_wheel_locations.extend([
53-
("/pywrap_external/libframework.2.dylib", "/pybind/libframework.2.dylib"),
60+
("/pywrap_external/libframework.2.dylib",
61+
"/pybind/libframework.2.dylib"),
5462
])
5563
else:
5664
relative_wheel_locations.extend([

pywrap_external/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pywrap_library(
1717
"//pybind",
1818
"//pybind:pybind_cc_only",
1919
"//pybind:pybind_copy",
20+
"//pybind/sub_pybind:relative_import_lib",
2021
],
2122
)
2223

0 commit comments

Comments
 (0)