Skip to content

test(helpers): keep the EGL device probe from importing pyglet.gl - #3027

Merged
Andy-Jost merged 2 commits into
NVIDIA:mainfrom
Andy-Jost:ajost/egl-probe-no-pyglet-gl-import
Oct 6, 2026
Merged

Andy-Jost merged 2 commits into
NVIDIA:mainfrom
Andy-Jost:ajost/egl-probe-no-pyglet-gl-import

Conversation

@Andy-Jost

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #3020. The narrowed catch imports pyglet.gl.lib.MissingFunctionException inside select_headless_egl_device_for_cuda, which runs before _configure_pyglet_headless() sets pyglet.options["headless_device"]. Importing pyglet.gl creates pyglet's shadow window (pyglet/gl/__init__.py imports pyglet.window at the bottom). In headless mode HeadlessDisplay.__init__ reads headless_device at that moment, and pyglet.display.get_display() returns the cached display afterwards, so the later headless_device assignment has no effect. The headless tests run in the shadow window's context, so on the multi-GPU systems #2865 fixed, the GL context lands on EGL device 0 again and cuGraphicsGLRegister* fails with the original CUDA_ERROR_INVALID_DEVICE. Before #3020 the probe imported only pyglet.libs.egl, which does not import pyglet.gl.

CI cannot detect this: no runner has an EGL device order that differs from the CUDA device order.

Changes

  • cuda_python_test_helpers/graphics.py: drop the pyglet.gl.lib import from the probe and match MissingFunctionException by module and name through a small helper, the same pattern is_gl_context_unavailable uses. Everything else still propagates. The module docstring now states the rule.
  • cuda_core/tests/test_graphics.py: add test_egl_device_probe_does_not_import_pyglet_gl, which runs the probe in a subprocess with headless set and asserts pyglet.gl is not in sys.modules.

Verification

Reproducible without a GPU on any Linux host with a libEGL (shown here with Mesa):

import pyglet
pyglet.options["headless"] = True
pyglet.options["headless_device"] = 99  # sentinel
from pyglet.gl.lib import MissingFunctionException
# ValueError: Invalid EGL device id: 99, raised from HeadlessDisplay.__init__ during the import

With the default headless_device, the import leaves a ShadowWindow and a cached HeadlessDisplay behind; the fixed probe leaves pyglet.gl unimported. The multi-GPU case itself was not re-run on hardware with a divergent device order.

Related Work

🤖 Generated with Claude Code

select_headless_egl_device_for_cuda runs before the graphics tests set
pyglet.options["headless_device"]. Importing pyglet.gl there creates
pyglet's shadow window, which in headless mode opens the EGL display on
device 0 and caches it, so the later headless_device assignment has no
effect and the GL context lands on EGL device 0 again (NVIDIA#2864).

Match MissingFunctionException by module and name instead, as
is_gl_context_unavailable already does, and add a subprocess test that
asserts the probe leaves pyglet.gl unimported.

Follow-up to NVIDIA#3020.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Andy-Jost Andy-Jost added this to the cuda.core 1.3.0 milestone Oct 5, 2026
@Andy-Jost Andy-Jost added bug Something isn't working P0 High priority - Must do! cuda.core Everything related to the cuda.core module labels Oct 5, 2026
@Andy-Jost Andy-Jost self-assigned this Oct 5, 2026
@Andy-Jost
Andy-Jost requested a review from juenglin October 5, 2026 23:07
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor
Doc Preview CI
Preview removed because the pull request was closed or merged.

CI does not install cuda_python_test_helpers; the parent imports it only
through the conftest sys.path fallback, which the -c child does not
inherit, so test_egl_device_probe_does_not_import_pyglet_gl failed in
every Linux row with ModuleNotFoundError. The child now gets a PYTHONPATH
that names the parent directory of the package the parent imported, and
a failure reports the child's stderr.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Improved headless EGL device detection so it can handle missing EGL functions without importing graphics code that may create an unintended shadow window.
    • Other EGL query errors continue to be reported rather than silently ignored.
  • Tests
    • Added coverage to verify the headless EGL probe runs without importing the graphics module that can trigger the shadow window.

Walkthrough

The EGL device probe no longer imports pyglet.gl to identify pyglet’s missing-function exception. A Linux/EGL-gated subprocess test checks that the probe leaves pyglet.gl unloaded.

Changes

Headless EGL probe

Layer / File(s) Summary
Exception handling and import-safety test
cuda_python_test_helpers/cuda_python_test_helpers/graphics.py, cuda_core/tests/test_graphics.py
The probe recognizes MissingFunctionException by its type metadata, returns None for that exception, and re-raises other exceptions. A subprocess test probes EGL device 0 and checks that pyglet.gl was not imported.

Suggested reviewers: rparolin

Priority: ⬇️ Low

Change: Bug fix

Merge Risk: 🔵 Low · up to 6dd84

The EGL regression test can stall on a system where device probing hangs. Add a subprocess timeout before merging, or accept this bounded test-run risk.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/cuda-python/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: cfa2f253-9dee-4a61-9ef2-0aa48edc8019
📥 Commits

Reviewing files that changed from the base of the PR and between ea58e1b and 6dd84a1.

📒 Files selected for processing (2)
  • cuda_core/tests/test_graphics.py
  • cuda_python_test_helpers/cuda_python_test_helpers/graphics.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Comment on lines +105 to +107
proc = subprocess.run( # noqa: S603 - trusted argv: this interpreter
[sys.executable, "-c", code], capture_output=True, text=True, env=env
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

suggestion: Bound the EGL probe subprocess. If EGL initialization or the probe blocks, subprocess.run waits indefinitely and stalls the test run. Set a timeout and report subprocess.TimeoutExpired as a test failure. Based on learnings: flag process-spawning calls without a timeout because a hung child can block indefinitely.

🧰 Tools
🪛 ast-grep (0.45.3)

[error] 104-106: Command coming from incoming request
Context: subprocess.run( # noqa: S603 - trusted argv: this interpreter
[sys.executable, "-c", code], capture_output=True, text=True, env=env
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

Source: Learnings

@Andy-Jost
Andy-Jost merged commit dbb33b6 into NVIDIA:main Oct 6, 2026
119 checks passed
@Andy-Jost
Andy-Jost deleted the ajost/egl-probe-no-pyglet-gl-import branch October 6, 2026 16:04
github-actions Bot pushed a commit that referenced this pull request Oct 7, 2026
Removed preview folders for the following PRs:
- PR #3022
- PR #3023
- PR #3027
- PR #3029
- PR #3036
- PR #3039
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working cuda.core Everything related to the cuda.core module P0 High priority - Must do!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants