Skip to content

Fix macOS Python 3.14 std::bad_any_cast by setting hidden visibility preset - #2041

Merged
jminor merged 1 commit into
AcademySoftwareFoundation:mainfrom
mcoliver:fix-macos-bad-any-cast
Sep 12, 2026
Merged

jminor merged 1 commit into
AcademySoftwareFoundation:mainfrom
mcoliver:fix-macos-bad-any-cast

Conversation

@mcoliver

@mcoliver mcoliver commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #2035.

When building the Python bindings (where the core libraries are linked statically into _otio.so), pybind11 aggressively forces -fvisibility=hidden. Because the core library targets lacked visibility configuration, they were compiled with -fvisibility=default.

On macOS, this mismatch generates duplicate type_info structures for template types like Retainer<>. Since macOS libc++ evaluates type_info::operator== via strict pointer equality, the dispatch table lookup inside otio_utils.cpp throws a std::bad_any_cast when crossing the boundary from pybind11 to the core library.

Note the reason this probably doesn't show up on Linux is because Linux's libstdc++ compares by name as a fallback

By setting CMAKE_CXX_VISIBILITY_PRESET hidden and CMAKE_VISIBILITY_INLINES_HIDDEN ON globally, the core library compiles with the exact same visibility rules as pybind11. The static linker then correctly collapses all type_info weak symbols into a single memory address, resolving the runtime exception on Python 3.14 macOS.

…std::any_cast pybind11 type_info mismatches across boundaries

Signed-off-by: Michael Oliver <mcoliver@gmail.com>
@linux-foundation-easycla

linux-foundation-easycla Bot commented Sep 11, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: mcoliver / name: Michael Oliver (6527f4e)

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.47%. Comparing base (bc5fe2d) to head (6527f4e).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2041      +/-   ##
==========================================
+ Coverage   83.25%   83.47%   +0.21%     
==========================================
  Files         180      182       +2     
  Lines       13479    13517      +38     
  Branches     1253     1254       +1     
==========================================
+ Hits        11222    11283      +61     
+ Misses       2085     2061      -24     
- Partials      172      173       +1     
Flag Coverage Δ
py-unittests 83.47% <ø> (+0.21%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 1 file with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 31e3101...6527f4e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jminor
jminor merged commit b3ebf10 into AcademySoftwareFoundation:main Sep 12, 2026
44 checks passed
darbyjohnston added a commit to darbyjohnston/OpenTimelineIO that referenced this pull request Sep 21, 2026
SerializableObject's destructor, its Reader's any overload and type
check, and its Writer's int64_t overload had no OTIO_API on them. A
build that hides its symbols by default, which main does since AcademySoftwareFoundation#2041,
leaves them out of the library and upgrade_downgrade_example cannot be
linked.

Signed-off-by: Darby Johnston <darbyjohnston@yahoo.com>
@darbyjohnston

Copy link
Copy Markdown
Contributor

@jminor This change breaks shared builds due to some missing exports. The symbols with missing exports get hidden by this which then cause linker errors. We should merge #2039 to fix the missing exports which might also make this change unnecessary. I also need to add a CI job that builds shared libraries to catch things like this.

darbyjohnston added a commit to darbyjohnston/OpenTimelineIO that referenced this pull request Sep 22, 2026
SerializableObject's destructor, its Reader's any overload and type
check, and its Writer's int64_t overload had no OTIO_API on them. A
build that hides its symbols by default, which main does since AcademySoftwareFoundation#2041,
leaves them out of the library and upgrade_downgrade_example cannot be
linked.

Signed-off-by: Darby Johnston <darbyjohnston@yahoo.com>
darbyjohnston added a commit that referenced this pull request Sep 22, 2026
* Export the API correctly for shared builds on Windows

OTIO_EXPORTS and OPENTIME_EXPORTS were PUBLIC, so everything consuming
OTIO compiled its API as dllexport where it has to be dllimport. They
become PRIVATE, which says the library is being built rather than
consumed; the OTIO_STATIC and OPENTIME_STATIC definitions stay PUBLIC,
since a consumer does have to know the API carries no declspec at all.

With that corrected the members marked only at the class level came up
missing, 74 of them, the Color statics and TypeRegistry among them:
OTIO_API_TYPE is empty on Windows, where only the per-member OTIO_API
carries the declspec. Those members are marked.

The three find_children templates lose OTIO_API in turn, a template
defined in a header being something that cannot be imported, and two
source files that define exported functions without including the
header that marks them are given the include.

No change to what either library exports on macOS or Linux: the
symbol tables of a shared build are identical before and after.

Signed-off-by: Darby Johnston <darbyjohnston@yahoo.com>

* Export the rest of what the examples link against

SerializableObject's destructor, its Reader's any overload and type
check, and its Writer's int64_t overload had no OTIO_API on them. A
build that hides its symbols by default, which main does since #2041,
leaves them out of the library and upgrade_downgrade_example cannot be
linked.

Signed-off-by: Darby Johnston <darbyjohnston@yahoo.com>

---------

Signed-off-by: Darby Johnston <darbyjohnston@yahoo.com>
@mcoliver

Copy link
Copy Markdown
Contributor Author

@darbyjohnston is correct. Apologies for not thinking deeper on the core issue. I was trusting the CI too much. I do think keeping the hidden visibility preset is good to have as a hardening measure in the event things are missed down the road but hopefully shared build CI should flag that.

@darbyjohnston

Copy link
Copy Markdown
Contributor

@mcoliver No problem, the exports are tricky to get right. I merged #2039 that should hopefully fix the missing ones. I also created a PR to add shared library builds to the CI to help catch these issues (#2057).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python 3.14 Throws a Runtime exception

4 participants