Source code for reapy.core.reaper.defer

"""Define reapy.defer and reapy.at_exit."""

import reapy

import os
import sys
import tempfile


[docs]class Deferrer: """Class to register and run deferred calls.""" _next_call_id = 0 _callbacks = {} _args = {} _kwargs = {} def _get_new_call_id(self): new_id = self._next_call_id Deferrer._next_call_id += 1 return new_id def _wrapped_open(self, *args, **kwargs): if args[0] == os.path.join(tempfile.gettempdir(), "reascripterr.txt"): return ReaperConsole() return open(*args, **kwargs)
[docs] def defer(self, callback, args, kwargs, at_exit=False): call_id = self._get_new_call_id() Deferrer._callbacks[call_id] = callback Deferrer._args[call_id] = args Deferrer._kwargs[call_id] = kwargs code = "import sys; sys.modules[{}].Deferrer().run({})".format( repr(__name__), call_id ) sys.modules["__main__"].open = self._wrapped_open if at_exit: sys.modules["__main__"].RPR_atexit(code) else: sys.modules["__main__"].RPR_defer(code)
[docs] def run(self, call_id): # Get callback and run it callback = self._callbacks[call_id] args = self._args[call_id] kwargs = self._kwargs[call_id] callback(*args, **kwargs) # Clean up sys.modules["__main__"].open = open for x in Deferrer._callbacks, Deferrer._args, Deferrer._kwargs: del x[call_id]
[docs]class ReaperConsole: """File-like wrapper around the Reaper Console."""
[docs] def close(self): pass
[docs] def flush(self): pass
[docs] def write(self, *args, **kwargs): reapy.print(*args, **kwargs)
[docs]def at_exit(f, *args, **kwargs): """ Make REAPER call a function after script execution. The function is also called if excution is terminated by user. Parameters ---------- f : callable Function to be called later. args : tuple, optional Positional arguments to pass to ``f``. kwargs : dict, optional Keyword arguments to pass to ``f``. Raises ------ AssertionError When called from outside REAPER. Examples -------- Typical use case of ``at_exit`` is cleaning up after a ``defer`` loop. The following example opens a file and starts a loop that indefinitely writes integers to that file. Since we want the file to be closed when the user terminates script execution, call to its ``close`` method is deferred to ``reapy.at_exit``. >>> import reapy >>> file = open("somefile.txt", "w") >>> def stupid_loop(i): ... file.write(i) ... reapy.defer(stupid_loop, i + 1) ... >>> reapy.at_exit(file.close) >>> stupid_loop(0) """ message = "reapy.at_exit can only be called inside REAPER." assert reapy.is_inside_reaper(), message Deferrer().defer(f, args, kwargs, at_exit=True)
[docs]def defer(f, *args, **kwargs): """ Make REAPER call a function later. Parameters ---------- f : callable Function to be called later. args : tuple, optional Positional arguments to pass to ``f``. kwargs : dict, optional Keyword arguments to pass to ``f``. Raises ------ AssertionError When called from outside REAPER. Notes ----- The average time before a defered call is actually run is about 0.03 seconds (around 30 defered calls are allowed per second). Examples -------- Typical use case of ``defer`` is running loops that don't block REAPER GUI. The following example creates a loop that indefinitely prints integers to the REAPER console, without blocking REAPER GUI. >>> import reapy >>> def stupid_loop(i): ... reapy.print(i) ... reapy.defer(stupid_loop, i + 1) ... >>> stupid_loop(0) """ # Check we are inside REAPER message = "reapy.defer can only be called inside REAPER." assert reapy.is_inside_reaper(), message Deferrer().defer(f, args, kwargs)