Skip to content

Commit

Permalink
Implement env vars (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
almarklein authored Nov 12, 2024
1 parent 5fdb7d8 commit 44c2c26
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 11 deletions.
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"

0 comments on commit 44c2c26

Please sign in to comment.