From adfe0d03ceea1f425246772e38ef16cf92e234f3 Mon Sep 17 00:00:00 2001 From: David Brochart Date: Tue, 8 Mar 2022 21:05:37 +0100 Subject: [PATCH] Restore pre/post_save_hook traits, add register_pre/post_save_hook() --- docs/source/developers/savehooks.rst | 3 --- .../services/contents/filemanager.py | 25 +++++++++++++------ .../services/contents/largefilemanager.py | 8 +++--- jupyter_server/services/contents/manager.py | 17 ++++++++++--- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/docs/source/developers/savehooks.rst b/docs/source/developers/savehooks.rst index d80113ceb8..900649d036 100644 --- a/docs/source/developers/savehooks.rst +++ b/docs/source/developers/savehooks.rst @@ -16,9 +16,6 @@ They are both called with keyword arguments:: pre_save_hook(model=model, path=path, contents_manager=cm) post_save_hook(model=model, os_path=os_path, contents_manager=cm) -Assigning to ``pre_save_hook`` (or ``post_save_hook``) multiple times will -register hooks, not override them. They will then be called sequentially. - Examples -------- diff --git a/jupyter_server/services/contents/filemanager.py b/jupyter_server/services/contents/filemanager.py index b5c3f55d18..4b38801a2c 100644 --- a/jupyter_server/services/contents/filemanager.py +++ b/jupyter_server/services/contents/filemanager.py @@ -82,16 +82,25 @@ def _validate_post_save_hook(self, proposal): value = import_item(value) if not callable(value): raise TraitError("post_save_hook must be callable") - self._post_save_hooks.append(value) return value + def register_post_save_hook(self, hook): + if isinstance(hook, str): + hook = import_item(hook) + if not callable(hook): + raise RuntimeError("post_save_hook must be callable") + self._post_save_hooks.append(hook) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._post_save_hooks = [] - def run_post_save_hook(self, model, os_path): - """Run the post-save hook if defined, and log errors""" - for post_save_hook in self._post_save_hooks: + def run_post_save_hooks(self, model, os_path): + """Run the post-save hooks if any, and log errors""" + post_save_hooks = self._post_save_hooks + if self.post_save_hook: + post_save_hooks.append(self.post_save_hook) + for post_save_hook in post_save_hooks: try: self.log.debug("Running post-save hook on %s", os_path) post_save_hook(os_path=os_path, model=model, contents_manager=self) @@ -461,7 +470,7 @@ def save(self, model, path=""): """Save the file model and return the model with no content.""" path = path.strip("/") - self.run_pre_save_hook(model=model, path=path) + self.run_pre_save_hooks(model=model, path=path) if "type" not in model: raise web.HTTPError(400, "No file type provided") @@ -501,7 +510,7 @@ def save(self, model, path=""): if validation_message: model["message"] = validation_message - self.run_post_save_hook(model=model, os_path=os_path) + self.run_post_save_hooks(model=model, os_path=os_path) return model @@ -787,7 +796,7 @@ async def save(self, model, path=""): os_path = self._get_os_path(path) self.log.debug("Saving %s", os_path) - self.run_pre_save_hook(model=model, path=path) + self.run_pre_save_hooks(model=model, path=path) if "type" not in model: raise web.HTTPError(400, "No file type provided") @@ -824,7 +833,7 @@ async def save(self, model, path=""): if validation_message: model["message"] = validation_message - self.run_post_save_hook(model=model, os_path=os_path) + self.run_post_save_hooks(model=model, os_path=os_path) return model diff --git a/jupyter_server/services/contents/largefilemanager.py b/jupyter_server/services/contents/largefilemanager.py index 3b404abf9f..85ad0099f7 100644 --- a/jupyter_server/services/contents/largefilemanager.py +++ b/jupyter_server/services/contents/largefilemanager.py @@ -18,7 +18,7 @@ def save(self, model, path=""): if chunk is not None: path = path.strip("/") - self.run_pre_save_hook(model=model, path=path) + self.run_pre_save_hooks(model=model, path=path) if "type" not in model: raise web.HTTPError(400, "No file type provided") @@ -52,7 +52,7 @@ def save(self, model, path=""): # Last chunk if chunk == -1: - self.run_post_save_hook(model=model, os_path=os_path) + self.run_post_save_hooks(model=model, os_path=os_path) return model else: return super(LargeFileManager, self).save(model, path) @@ -91,7 +91,7 @@ async def save(self, model, path=""): os_path = self._get_os_path(path) self.log.debug("Saving %s", os_path) - self.run_pre_save_hook(model=model, path=path) + self.run_pre_save_hooks(model=model, path=path) if "type" not in model: raise web.HTTPError(400, "No file type provided") @@ -122,7 +122,7 @@ async def save(self, model, path=""): # Last chunk if chunk == -1: - self.run_post_save_hook(model=model, os_path=os_path) + self.run_post_save_hooks(model=model, os_path=os_path) return model else: return await super(AsyncLargeFileManager, self).save(model, path) diff --git a/jupyter_server/services/contents/manager.py b/jupyter_server/services/contents/manager.py index e074697f9b..c73718a962 100644 --- a/jupyter_server/services/contents/manager.py +++ b/jupyter_server/services/contents/manager.py @@ -126,16 +126,25 @@ def _validate_pre_save_hook(self, proposal): value = import_item(self.pre_save_hook) if not callable(value): raise TraitError("pre_save_hook must be callable") - self._pre_save_hooks.append(value) return value + def register_pre_save_hook(self, hook): + if isinstance(hook, str): + hook = import_item(hook) + if not callable(hook): + raise RuntimeError("pre_save_hook must be callable") + self._pre_save_hooks.append(hook) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._pre_save_hooks = [] - def run_pre_save_hook(self, model, path, **kwargs): - """Run the pre-save hook if defined, and log errors""" - for pre_save_hook in self._pre_save_hooks: + def run_pre_save_hooks(self, model, path, **kwargs): + """Run the pre-save hooks if defined, and log errors""" + pre_save_hooks = self._pre_save_hooks + if self.pre_save_hook: + pre_save_hooks.append(self.pre_save_hook) + for pre_save_hook in pre_save_hooks: try: self.log.debug("Running pre-save hook on %s", path) pre_save_hook(model=model, path=path, contents_manager=self, **kwargs)