Skip to content

Commit b28789c

Browse files
committed
Give a better error when a context is used after it's closed.
1 parent af6d8ea commit b28789c

File tree

2 files changed

+23
-11
lines changed

2 files changed

+23
-11
lines changed

tcod/context.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,18 @@ def __init__(self, context_p: Any) -> None:
176176

177177
@classmethod
178178
def _claim(cls, context_p: Any) -> Context:
179+
"""Return a new instance wrapping a context pointer."""
179180
return cls(ffi.gc(context_p, lib.TCOD_context_delete))
180181

182+
@property
183+
def _p(self) -> Any: # noqa: ANN401
184+
"""Return the context pointer or raise if it is missing."""
185+
try:
186+
return self._context_p
187+
except AttributeError:
188+
msg = "This context has been closed can no longer be used."
189+
raise RuntimeError(msg) from None
190+
181191
def __enter__(self) -> Context:
182192
"""Enter this context which will close on exiting."""
183193
return self
@@ -236,18 +246,18 @@ def present(
236246
"align_y": align[1],
237247
},
238248
)
239-
_check(lib.TCOD_context_present(self._context_p, console.console_c, viewport_args))
249+
_check(lib.TCOD_context_present(self._p, console.console_c, viewport_args))
240250

241251
def pixel_to_tile(self, x: int, y: int) -> tuple[int, int]:
242252
"""Convert window pixel coordinates to tile coordinates."""
243253
with ffi.new("int[2]", (x, y)) as xy:
244-
_check(lib.TCOD_context_screen_pixel_to_tile_i(self._context_p, xy, xy + 1))
254+
_check(lib.TCOD_context_screen_pixel_to_tile_i(self._p, xy, xy + 1))
245255
return xy[0], xy[1]
246256

247257
def pixel_to_subtile(self, x: int, y: int) -> tuple[float, float]:
248258
"""Convert window pixel coordinates to sub-tile coordinates."""
249259
with ffi.new("double[2]", (x, y)) as xy:
250-
_check(lib.TCOD_context_screen_pixel_to_tile_d(self._context_p, xy, xy + 1))
260+
_check(lib.TCOD_context_screen_pixel_to_tile_d(self._p, xy, xy + 1))
251261
return xy[0], xy[1]
252262

253263
def convert_event(self, event: _Event) -> _Event:
@@ -286,7 +296,7 @@ def convert_event(self, event: _Event) -> _Event:
286296
def save_screenshot(self, path: str | None = None) -> None:
287297
"""Save a screen-shot to the given file path."""
288298
c_path = path.encode("utf-8") if path is not None else ffi.NULL
289-
_check(lib.TCOD_context_save_screenshot(self._context_p, c_path))
299+
_check(lib.TCOD_context_save_screenshot(self._p, c_path))
290300

291301
def change_tileset(self, tileset: tcod.tileset.Tileset | None) -> None:
292302
"""Change the active tileset used by this context.
@@ -300,7 +310,7 @@ def change_tileset(self, tileset: tcod.tileset.Tileset | None) -> None:
300310
Using this method only one tileset is active per-frame.
301311
See :any:`tcod.render` if you want to renderer with multiple tilesets in a single frame.
302312
"""
303-
_check(lib.TCOD_context_change_tileset(self._context_p, _handle_tileset(tileset)))
313+
_check(lib.TCOD_context_change_tileset(self._p, _handle_tileset(tileset)))
304314

305315
def new_console(
306316
self,
@@ -359,7 +369,7 @@ def new_console(
359369
if magnification < 0:
360370
raise ValueError("Magnification must be greater than zero. (Got %f)" % magnification)
361371
size = ffi.new("int[2]")
362-
_check(lib.TCOD_context_recommended_console_size(self._context_p, magnification, size, size + 1))
372+
_check(lib.TCOD_context_recommended_console_size(self._p, magnification, size, size + 1))
363373
width, height = max(min_columns, size[0]), max(min_rows, size[1])
364374
return tcod.console.Console(width, height, order=order)
365375

@@ -371,13 +381,13 @@ def recommended_console_size(self, min_columns: int = 1, min_rows: int = 1) -> t
371381
If result is only used to create a new console then you may want to call :any:`Context.new_console` instead.
372382
"""
373383
with ffi.new("int[2]") as size:
374-
_check(lib.TCOD_context_recommended_console_size(self._context_p, 1.0, size, size + 1))
384+
_check(lib.TCOD_context_recommended_console_size(self._p, 1.0, size, size + 1))
375385
return max(min_columns, size[0]), max(min_rows, size[1])
376386

377387
@property
378388
def renderer_type(self) -> int:
379389
"""Return the libtcod renderer type used by this context."""
380-
return _check(lib.TCOD_context_get_renderer_type(self._context_p))
390+
return _check(lib.TCOD_context_get_renderer_type(self._p))
381391

382392
@property
383393
def sdl_window_p(self) -> Any:
@@ -407,7 +417,7 @@ def toggle_fullscreen(context: tcod.context.Context) -> None:
407417
)
408418
409419
'''
410-
return lib.TCOD_context_get_sdl_window(self._context_p)
420+
return lib.TCOD_context_get_sdl_window(self._p)
411421

412422
@property
413423
def sdl_window(self) -> tcod.sdl.video.Window | None:
@@ -439,7 +449,7 @@ def sdl_renderer(self) -> tcod.sdl.render.Renderer | None:
439449
440450
.. versionadded:: 13.4
441451
"""
442-
p = lib.TCOD_context_get_sdl_renderer(self._context_p)
452+
p = lib.TCOD_context_get_sdl_renderer(self._p)
443453
return tcod.sdl.render.Renderer(p) if p else None
444454

445455
@property
@@ -448,7 +458,7 @@ def sdl_atlas(self) -> tcod.render.SDLTilesetAtlas | None:
448458
449459
.. versionadded:: 13.5
450460
"""
451-
if self._context_p.type not in (lib.TCOD_RENDERER_SDL, lib.TCOD_RENDERER_SDL2):
461+
if self._p.type not in (lib.TCOD_RENDERER_SDL, lib.TCOD_RENDERER_SDL2):
452462
return None
453463
context_data = ffi.cast("struct TCOD_RendererSDL2*", self._context_p.contextdata_)
454464
return tcod.render.SDLTilesetAtlas._from_ref(context_data.renderer, context_data.atlas)

tests/test_tcod.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,5 @@ def test_context() -> None:
198198
context.change_tileset(tcod.tileset.Tileset(16, 16))
199199
context.pixel_to_tile(0, 0)
200200
context.pixel_to_subtile(0, 0)
201+
with pytest.raises(RuntimeError, match=".*context has been closed"):
202+
context.present(console)

0 commit comments

Comments
 (0)