Skip to content

Implement env vars #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion docs/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,27 @@ subclass implementing a remote frame-buffer. There are also some `wgpu examples
canvas # Use as cell output



.. _env_vars:

Selecting a backend with env vars
---------------------------------

The automatic backend selection can be influenced with the use of environment
variables. This makes it possible to e.g. create examples using the
auto-backend, and allow these examples to run on CI with the offscreen backend.
Note that once ``rendercanvas.auto`` is imported, the selection has been made,
and importing it again always yields the same backend.

* ``RENDERCANVAS_BACKEND``: Set the name of the backend that the auto-backend should select. Case insensituve.
* ``RENDERCANVAS_FORCE_OFFSCREEN``: force the auto-backend to select the offscreen canvas, ignoring the above env var. Truethy values are '1', 'true', and 'yes'.

Rendercanvas also supports the following env vars for backwards compatibility, but only when the corresponding ``RENDERCANVAS_`` env var is unset or an empty string:

* ``WGPU_GUI_BACKEND``: legacy alias.
* ``WGPU_FORCE_OFFSCREEN``: legacy alias.


.. _interactive_use:

Interactive use
Expand All @@ -152,7 +173,7 @@ honor that and use Qt instead.
On ``jupyter console`` and ``qtconsole``, the kernel is the same as in ``jupyter notebook``,
making it (about) impossible to tell that we cannot actually use
ipywidgets. So it will try to use ``jupyter_rfb``, but cannot render anything.
It's therefore advised to either use ``%gui qt`` or set the ``WGPU_GUI_BACKEND`` env var
It's therefore advised to either use ``%gui qt`` or set the ``RENDERCANVAS_BACKEND`` env var
to "glfw". The latter option works well, because these kernels *do* have a
running asyncio event loop!

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
ROOT_DIR = os.path.abspath(os.path.join(__file__, "..", ".."))
sys.path.insert(0, ROOT_DIR)

os.environ["WGPU_FORCE_OFFSCREEN"] = "true"
os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = "true"


# Load wglibu so autodoc can query docstrings
Expand Down
28 changes: 22 additions & 6 deletions rendercanvas/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,35 @@ def backends_generator():

def backends_by_env_vars():
"""Generate backend names set via one the supported environment variables."""

# We also support the legacy WGPU_X env vars, but only when the
# corresponding RENDERCANVAS_X is not set or set to the empty string.

def get_env_var(*varnames):
for varname in varnames:
value = os.getenv(varname, "").lower()
if value:
return value, varname
else:
return "", varnames[0]

# Env var intended for testing, overrules everything else
if os.environ.get("WGPU_FORCE_OFFSCREEN", "").lower() in ("1", "true", "yes"):
yield "offscreen", "WGPU_FORCE_OFFSCREEN is set"
force_offscreen, varname = get_env_var(
"RENDERCANVAS_FORCE_OFFSCREEN", "WGPU_FORCE_OFFSCREEN"
)
if force_offscreen and force_offscreen in ("1", "true", "yes"):
yield "offscreen", f"{varname} is set"

# Env var to force a backend for general use
backend_name = os.getenv("WGPU_GUI_BACKEND", "").lower().strip() or None
backend_name, varname = get_env_var("RENDERCANVAS_BACKEND", "WGPU_GUI_BACKEND")
if backend_name:
if backend_name not in BACKEND_NAMES:
logger.warning(
f"Ignoring invalid WGPU_GUI_BACKEND '{backend_name}', must be one of {BACKEND_NAMES}"
f"Ignoring invalid {varname} '{backend_name}', must be one of {BACKEND_NAMES}"
)
backend_name = None
if backend_name:
yield backend_name, "WGPU_GUI_BACKEND is set"
yield backend_name, f"{varname} is set"


def backends_by_jupyter():
Expand All @@ -116,7 +132,7 @@ def backends_by_jupyter():
# whether we're in a console or notebook. Technically this kernel could be
# connected to a client of each. So we assume that ipywidgets can be used.
# User on jupyter console (or similar) should ``%gui qt`` or set
# WGPU_GUI_BACKEND to 'glfw'.
# RENDERCANVAS_BACKEND to 'glfw'.

# If GUI integration is enabled, we select the corresponding backend instead of jupyter
app = getattr(ip.kernel, "app", None)
Expand Down
36 changes: 34 additions & 2 deletions tests/test_offscreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,36 @@
def test_offscreen_selection_using_env_var():
from rendercanvas.offscreen import ManualOffscreenRenderCanvas

ori = os.environ.get("WGPU_FORCE_OFFSCREEN", "")
ori = os.getenv("RENDERCANVAS_FORCE_OFFSCREEN")
os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = "1"

# We only need the func, but this triggers the auto-import
from rendercanvas.auto import select_backend

try:
if not os.getenv("CI"):
for value in ["", "0", "false", "False", "wut"]:
os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = value
module = select_backend()
assert module.RenderCanvas is not ManualOffscreenRenderCanvas

for value in ["1", "true", "True"]:
os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = value
module = select_backend()
assert module.RenderCanvas is ManualOffscreenRenderCanvas

finally:
if ori is not None:
os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = ori


def test_offscreen_selection_using_legacyt_env_var():
from rendercanvas.offscreen import ManualOffscreenRenderCanvas

ori1 = os.getenv("RENDERCANVAS_FORCE_OFFSCREEN")
ori2 = os.getenv("WGPU_FORCE_OFFSCREEN")

os.environ.pop("RENDERCANVAS_FORCE_OFFSCREEN", None)
os.environ["WGPU_FORCE_OFFSCREEN"] = "1"

# We only need the func, but this triggers the auto-import
Expand All @@ -31,7 +60,10 @@ def test_offscreen_selection_using_env_var():
assert module.RenderCanvas is ManualOffscreenRenderCanvas

finally:
os.environ["WGPU_FORCE_OFFSCREEN"] = ori
if ori1 is not None:
os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = ori1
if ori2 is not None:
os.environ["WGPU_FORCE_OFFSCREEN"] = ori2


def test_offscreen_event_loop():
Expand Down
2 changes: 1 addition & 1 deletion tests/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,5 @@ def _determine_can_use_glfw():
can_use_wgpu_lib = bool(adapter_summary)

can_use_glfw = _determine_can_use_glfw()
is_ci = bool(os.getenv("CI", None))
is_ci = bool(os.getenv("CI"))
is_pypy = sys.implementation.name == "pypy"