Skip to content

Commit 203cc26

Browse files
authored
[smart_holder] Add test_namespace_visibility (#4050)
* Add test_namespace_visibility To probe environment/toolchain/platform-specific behavior under the exact same conditions as normal tests. (An earlier version of this code was used to inform PR #4043.) * Disable flake8 in ubench/holder_comparison_*.py, to suppress new & useless diagnostics. * Disable namespace_visibility_1s.cpp (tosee if that resolves the MSVC and CUDA `test_cross_module_exception_translator` failures). * Turn off flake8 completely for ubench (the Strip unnecessary `# noqa`s action un-helpfully removed the added noqa). * Disable test_namespace_visibility completely. Just keep the two .cpp files, only setting the module docstring and doing nothing else. * Rename test_namespace_visibility.py to test_exc_namespace_visibility.py, so that it is imported by pytest before test_exceptions.py * Add `set_property(SOURCE namespace_visibility_1s.cpp PROPERTY LANGUAGE CUDA)` * Add reference to PR #4054 * Complete the documentation (comments in test_exc_namespace_visibility.py). * Rename namespace_visibility.h to namespace_visibility.inl, as suggested by @charlesbeattie
1 parent bcd1800 commit 203cc26

6 files changed

+167
-2
lines changed

tests/CMakeLists.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ set(PYBIND11_TEST_FILES
148148
test_eigen
149149
test_enum
150150
test_eval
151+
test_exc_namespace_visibility.py
151152
test_exceptions
152153
test_factory_constructors
153154
test_gil_scoped
@@ -236,6 +237,8 @@ tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already
236237
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
237238
tests_extra_targets("test_class_sh_module_local.py"
238239
"class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2")
240+
tests_extra_targets("test_exc_namespace_visibility.py"
241+
"namespace_visibility_1;namespace_visibility_2")
239242

240243
set(PYBIND11_EIGEN_REPO
241244
"https://gitlab.com/libeigen/eigen.git"
@@ -426,8 +429,14 @@ if(PYBIND11_CUDA_TESTS)
426429
endif()
427430

428431
foreach(target ${test_targets})
429-
set(test_files ${PYBIND11_TEST_FILES})
430-
if(NOT "${target}" STREQUAL "pybind11_tests")
432+
if("${target}" STREQUAL "pybind11_tests")
433+
set(test_files ${PYBIND11_TEST_FILES})
434+
elseif("${target}" STREQUAL "namespace_visibility_1")
435+
set(test_files namespace_visibility_1s.cpp)
436+
if(PYBIND11_CUDA_TESTS)
437+
set_property(SOURCE namespace_visibility_1s.cpp PROPERTY LANGUAGE CUDA)
438+
endif()
439+
else()
431440
set(test_files "")
432441
endif()
433442

tests/namespace_visibility.inl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2022 The Pybind Development Team.
2+
// All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <cstddef>
8+
9+
#ifdef __GNUG__
10+
# define PYBIND11_NS_VIS_U /* unspecified */
11+
# define PYBIND11_NS_VIS_H __attribute__((visibility("hidden")))
12+
#else
13+
# define PYBIND11_NS_VIS_U
14+
# define PYBIND11_NS_VIS_H
15+
#endif
16+
17+
#define PYBIND11_NS_VIS_FUNC \
18+
inline std::ptrdiff_t func() { \
19+
static std::ptrdiff_t value = 0; \
20+
return reinterpret_cast<std::ptrdiff_t>(&value); \
21+
}
22+
23+
#define PYBIND11_NS_VIS_DEFS \
24+
m.def("ns_vis_uuu_func", pybind11_ns_vis_uuu::func); \
25+
m.def("ns_vis_uuh_func", pybind11_ns_vis_uuh::func); \
26+
m.def("ns_vis_uhu_func", pybind11_ns_vis_uhu::func); \
27+
m.def("ns_vis_uhh_func", pybind11_ns_vis_uhh::func); \
28+
m.def("ns_vis_huu_func", pybind11_ns_vis_huu::func); \
29+
m.def("ns_vis_huh_func", pybind11_ns_vis_huh::func); \
30+
m.def("ns_vis_hhu_func", pybind11_ns_vis_hhu::func); \
31+
m.def("ns_vis_hhh_func", pybind11_ns_vis_hhh::func);

tests/namespace_visibility_1.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include "pybind11/pybind11.h"
2+
#include "namespace_visibility.inl"
3+
4+
// clang-format off
5+
namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
6+
namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
7+
namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
8+
namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
9+
namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
10+
namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
11+
namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
12+
namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
13+
// ^ ^
14+
// bit used .............. here
15+
// clang-format on
16+
17+
void namespace_visibility_1s(pybind11::module_ &m);
18+
19+
PYBIND11_MODULE(namespace_visibility_1, m) {
20+
PYBIND11_NS_VIS_DEFS
21+
22+
auto sm = m.def_submodule("submodule");
23+
namespace_visibility_1s(sm);
24+
}

tests/namespace_visibility_1s.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "pybind11/pybind11.h"
2+
#include "namespace_visibility.inl"
3+
4+
// clang-format off
5+
namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
6+
namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
7+
namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
8+
namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
9+
namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
10+
namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
11+
namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
12+
namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
13+
// ^ ^
14+
// bit used ............. here
15+
// clang-format on
16+
17+
void namespace_visibility_1s(pybind11::module_ &m) { PYBIND11_NS_VIS_DEFS }

tests/namespace_visibility_2.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "pybind11/pybind11.h"
2+
#include "namespace_visibility.inl"
3+
4+
// clang-format off
5+
namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
6+
namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
7+
namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
8+
namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
9+
namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
10+
namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
11+
namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC }
12+
namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC }
13+
// ^ ^
14+
// bit used ............ here
15+
// clang-format on
16+
17+
PYBIND11_MODULE(namespace_visibility_2, m) { PYBIND11_NS_VIS_DEFS }
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# This is not really a unit test, but probing environment/toolchain/platform-specific
2+
# behavior under the exact same conditions as normal tests.
3+
# The results are useful to understanding the effects of, e.g., removing
4+
# `-fvisibility=hidden` or `__attribute__((visibility("hidden")))`, or linking
5+
# extensions statically with the core Python interpreter.
6+
7+
# NOTE
8+
# ====
9+
# The "exc_" in "test_exc_namespace_visibility.py" is a workaround, to avoid a
10+
# test_cross_module_exception_translator (test_exceptions.py) failure. This
11+
# test has to be imported (by pytest) before test_exceptions.py; pytest sorts
12+
# lexically. See https://github.com/pybind/pybind11/pull/4054 for more information.
13+
14+
import itertools
15+
16+
import namespace_visibility_1
17+
import namespace_visibility_2
18+
import pytest
19+
20+
# Please take a quick look at namespace_visibility.h first, to see what is being probed.
21+
#
22+
# EXPECTED is for -fvisibility=hidden or equivalent, as recommended in the docs.
23+
EXPECTED_ALL_UNIQUE_POINTERS_OBSERVED = "AAC:AAc:AaC:Aac:aAC:aAc:aaC:aac"
24+
# ^^^
25+
# namespace_visibility_1 pointer|||
26+
# namespace_visibility_1.submodule pointer|| (identical letters means same pointer)
27+
# namespace_visibility_2 pointer|
28+
# Upper-case: namespace visibility unspecified
29+
# Lower-case: namespace visibility hidden
30+
# This test probes all 2**3 combinations of u/h ** number-of-sub/modules.
31+
#
32+
# Also observed:
33+
# AAA:AAc:AaC:Aac:aAC:aAc:aaC:aac -fvisibility=default Linux
34+
# AAA:AAc:AaA:Aac:aAC:aAc:aaC:aac -fvisibility=default macOS
35+
# AAA:AAa:AaA:Aaa:aAA:aAa:aaA:aaa everything linked statically
36+
37+
38+
def test_namespace_visibility():
39+
modules = (
40+
namespace_visibility_1,
41+
namespace_visibility_1.submodule,
42+
namespace_visibility_2,
43+
)
44+
unique_pointer_labels = "ABC"
45+
unique_pointers_observed = []
46+
# u = visibility unspecified
47+
# h = visibility hidden
48+
for visibility in itertools.product(*([("u", "h")] * len(modules))):
49+
# See functions in namespace_visibility_*.cpp
50+
func = "ns_vis_" + "".join(visibility) + "_func"
51+
ptrs = []
52+
uq_ptrs_obs = ""
53+
for vis, m in zip(visibility, modules):
54+
ptr = getattr(m, func)()
55+
ptrs.append(ptr)
56+
lbl = unique_pointer_labels[ptrs.index(ptr)]
57+
if vis == "h":
58+
# Encode u/h info as upper/lower case to make the final result
59+
# as compact as possible.
60+
lbl = lbl.lower()
61+
uq_ptrs_obs += lbl
62+
unique_pointers_observed.append(uq_ptrs_obs)
63+
all_unique_pointers_observed = ":".join(unique_pointers_observed)
64+
if all_unique_pointers_observed != EXPECTED_ALL_UNIQUE_POINTERS_OBSERVED:
65+
pytest.skip(
66+
f"UNUSUAL all_unique_pointers_observed: {all_unique_pointers_observed}"
67+
)

0 commit comments

Comments
 (0)