diff --git a/bigframes/display/__init__.py b/bigframes/display/__init__.py index 97248a0efb..aa1371db56 100644 --- a/bigframes/display/__init__.py +++ b/bigframes/display/__init__.py @@ -16,11 +16,33 @@ from __future__ import annotations -try: - import anywidget # noqa +from typing import Any - from bigframes.display.anywidget import TableWidget - __all__ = ["TableWidget"] -except Exception: - pass +def __getattr__(name: str) -> Any: + """Lazily import TableWidget to avoid ZMQ port conflicts. + + anywidget and traitlets eagerly initialize kernel communication channels on + import. This can lead to race conditions and ZMQ port conflicts when + multiple Jupyter kernels are started in parallel, such as during notebook + tests. By using __getattr__, we defer the import of TableWidget until it is + explicitly accessed, preventing premature initialization and avoiding port + collisions. + """ + if name == "TableWidget": + try: + import anywidget # noqa + + from bigframes.display.anywidget import TableWidget + + return TableWidget + except Exception: + raise AttributeError( + f"module '{__name__}' has no attribute '{name}'. " + "TableWidget requires anywidget and traitlets to be installed. " + "Please `pip install anywidget traitlets` or `pip install 'bigframes[anywidget]'`." + ) + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + + +__all__ = ["TableWidget"]