From 444d9bd731cee04869a04d1f150e2611f57f7515 Mon Sep 17 00:00:00 2001 From: pbailly Date: Thu, 25 Feb 2021 21:59:20 +0100 Subject: [PATCH 01/11] Use the new REST API for Jupyter Notebook State. [ch61633] --- dataikuapi/dss/notebook.py | 17 +++++------------ dataikuapi/dss/project.py | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index 317531ba..c23e5a6a 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -29,20 +29,13 @@ def unload(self, session_id=None): return self.client._perform_json("DELETE", "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) - def get_state(self, refresh=False): + def get_state(self): """ - Get the status of this Jupyter notebook - - :param bool refresh: if True, get the status of the notebook from the backend + Get the metadata associated to this Jupyter notebook """ - notebook_states = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/" % self.project_key, - params={"active": False}) - if self.state is None or refresh: - for notebook_state in notebook_states: - if notebook_state.get("name") == self.notebook_name: - self.state = notebook_state - break + self.state = self.client._perform_json("GET", + "/projects/%s/jupyter-notebook-states/%s" % (self.project_key, self.notebook_name), + params={"active": False}) return self.state def get_sessions(self): diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index 3c74d31b..73421bc0 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -841,7 +841,7 @@ def list_jupyter_notebooks(self, as_objects=True, active=False): :returns: The list of the notebooks - see as_objects for more information :rtype: list """ - notebooks = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/" % self.project_key, + notebooks = self.client._perform_json("GET", "/projects/%s/jupyter-notebook-states/" % self.project_key, params={"active": active}) if as_objects: return [DSSNotebook(self.client, notebook_state['projectKey'], notebook_state['name'], state=notebook_state) for notebook_state in notebooks] From ac13aea21e376d779a97696424d671beb5116951 Mon Sep 17 00:00:00 2001 From: pbailly Date: Fri, 26 Feb 2021 11:13:41 +0100 Subject: [PATCH 02/11] Apply review. [ch61633] --- dataikuapi/dss/notebook.py | 7 +++---- dataikuapi/dss/project.py | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index c23e5a6a..5124dce3 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -27,15 +27,14 @@ def unload(self, session_id=None): else: session_id = sessions[0].get('sessionId', None) return self.client._perform_json("DELETE", - "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) + "/projects/%s/jupyter-notebook-states/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) def get_state(self): """ Get the metadata associated to this Jupyter notebook """ self.state = self.client._perform_json("GET", - "/projects/%s/jupyter-notebook-states/%s" % (self.project_key, self.notebook_name), - params={"active": False}) + "/projects/%s/jupyter-notebook-states/%s" % (self.project_key, self.notebook_name)) return self.state def get_sessions(self): @@ -46,7 +45,7 @@ def get_sessions(self): if self.state is None: self.state = {} sessions = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/%s/sessions" % (self.project_key, self.notebook_name)) + "/projects/%s/jupyter-notebook-states/%s/sessions" % (self.project_key, self.notebook_name)) self.state["activeSessions"] = sessions return sessions diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index 73421bc0..e5fdaa60 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -841,8 +841,7 @@ def list_jupyter_notebooks(self, as_objects=True, active=False): :returns: The list of the notebooks - see as_objects for more information :rtype: list """ - notebooks = self.client._perform_json("GET", "/projects/%s/jupyter-notebook-states/" % self.project_key, - params={"active": active}) + notebooks = self.client._perform_json("GET", "/projects/%s/jupyter-notebook-states/" % self.project_key) if as_objects: return [DSSNotebook(self.client, notebook_state['projectKey'], notebook_state['name'], state=notebook_state) for notebook_state in notebooks] else: From 2d123af4285f5b915dc6b08bace0fb8a0d785356 Mon Sep 17 00:00:00 2001 From: pbailly Date: Fri, 26 Feb 2021 15:18:52 +0100 Subject: [PATCH 03/11] Prevent empty save. --- dataikuapi/dss/notebook.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index 5124dce3..6229dcfd 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -62,6 +62,8 @@ def save(self): """ Save the content of this Jupyter notebook """ + if self.content is None: + raise ValueError("Notebook content is empty, use \"get_content()\" or manually set the content of the notebook before saving") return self.client._perform_json("PUT", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name), body=self.content) From 7f1e16ade876985a2f2348bc2f932a711ea142c3 Mon Sep 17 00:00:00 2001 From: pbailly Date: Tue, 2 Mar 2021 11:16:08 +0100 Subject: [PATCH 04/11] Rework API [ch61633] --- dataikuapi/dss/notebook.py | 29 +++++++++++------------------ dataikuapi/dss/project.py | 18 ++++++++++++------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index 6229dcfd..d9cc4bd0 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -1,4 +1,5 @@ from .discussion import DSSObjectDiscussions +from .notebookcontent import DSSNotebookContent class DSSNotebook(object): """ @@ -9,8 +10,10 @@ def __init__(self, client, project_key, notebook_name, state=None, content=None) self.project_key = project_key self.notebook_name = notebook_name self.state = state - self.content = content - self.state_is_peek = True + if content: + self.content = content + else: + self.content = DSSNotebookContent(self.client, self.project_key, self.notebook_name) def unload(self, session_id=None): """ @@ -27,14 +30,17 @@ def unload(self, session_id=None): else: session_id = sessions[0].get('sessionId', None) return self.client._perform_json("DELETE", - "/projects/%s/jupyter-notebook-states/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) + "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) def get_state(self): """ + Deprecated. Use self.get_content().get_metadata() Get the metadata associated to this Jupyter notebook """ + import warnings + warnings.warn("Use self.get_content().get_metadata()", DeprecationWarning) self.state = self.client._perform_json("GET", - "/projects/%s/jupyter-notebook-states/%s" % (self.project_key, self.notebook_name)) + "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, self.notebook_name)) return self.state def get_sessions(self): @@ -45,7 +51,7 @@ def get_sessions(self): if self.state is None: self.state = {} sessions = self.client._perform_json("GET", - "/projects/%s/jupyter-notebook-states/%s/sessions" % (self.project_key, self.notebook_name)) + "/projects/%s/jupyter-notebooks/%s/sessions" % (self.project_key, self.notebook_name)) self.state["activeSessions"] = sessions return sessions @@ -53,21 +59,8 @@ def get_content(self): """ Get the content of this Jupyter notebook (metadata, cells, nbformat) """ - if self.content is None: - self.content = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) return self.content - def save(self): - """ - Save the content of this Jupyter notebook - """ - if self.content is None: - raise ValueError("Notebook content is empty, use \"get_content()\" or manually set the content of the notebook before saving") - return self.client._perform_json("PUT", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name), - body=self.content) - def delete(self): """ Delete this Jupyter notebook and stop all of its active sessions. diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index e5fdaa60..e810fb6c 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -1,5 +1,6 @@ import time, warnings, sys, os.path as osp from .dataset import DSSDataset, DSSDatasetListItem, DSSManagedDatasetCreationHelper +from .notebookcontent import DSSNotebookContent from .streaming_endpoint import DSSStreamingEndpoint, DSSStreamingEndpointListItem, DSSManagedStreamingEndpointCreationHelper from .recipe import DSSRecipeListItem, DSSRecipe from . import recipe @@ -841,7 +842,10 @@ def list_jupyter_notebooks(self, as_objects=True, active=False): :returns: The list of the notebooks - see as_objects for more information :rtype: list """ - notebooks = self.client._perform_json("GET", "/projects/%s/jupyter-notebook-states/" % self.project_key) + notebook_names = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/" % self.project_key) + notebooks = [] + for notebook_name in notebook_names: + notebooks.append(self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name))) if as_objects: return [DSSNotebook(self.client, notebook_state['projectKey'], notebook_state['name'], state=notebook_state) for notebook_state in notebooks] else: @@ -853,11 +857,10 @@ def get_jupyter_notebook(self, notebook_name): :param str notebook_name: The name of the jupyter notebook to retrieve :returns: A handle to interact with this jupyter notebook - :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebook` jupyter notebook handle + :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebookContent` jupyter notebook handle """ - notebook_content = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, notebook_name)) - return DSSNotebook(self.client, self.project_key, notebook_name, content=notebook_content) + notebook_state = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name)) + return DSSNotebook(self.client, self.project_key, notebook_name, state=notebook_state) def create_jupyter_notebook(self, notebook_name, notebook_content): """ @@ -873,7 +876,10 @@ def create_jupyter_notebook(self, notebook_name, notebook_content): created_notebook_content = self.client._perform_json("POST", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, notebook_name), body=notebook_content) - return DSSNotebook(self.client, self.project_key, notebook_name, content=created_notebook_content) + # Retrieve back the metadata after creation + notebook_state = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name)) + return DSSNotebook(self.client, self.project_key, notebook_name, state=notebook_state, + content=DSSNotebookContent(self.client, self.project_key, notebook_name, created_notebook_content)) ######################################################## # Continuous activities From b44a921c2831b606e1b54a4c91d8b924e378ba5a Mon Sep 17 00:00:00 2001 From: pbailly Date: Tue, 2 Mar 2021 11:21:34 +0100 Subject: [PATCH 05/11] Add missing file. [ch61633] --- dataikuapi/dss/notebookcontent.py | 46 +++++++++++++++++++++++++++++++ dataikuapi/dss/project.py | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 dataikuapi/dss/notebookcontent.py diff --git a/dataikuapi/dss/notebookcontent.py b/dataikuapi/dss/notebookcontent.py new file mode 100644 index 00000000..dff3e423 --- /dev/null +++ b/dataikuapi/dss/notebookcontent.py @@ -0,0 +1,46 @@ +from .discussion import DSSObjectDiscussions + +class DSSNotebookContent(object): + """ + A Python/R/Scala notebook on the DSS instance + """ + def __init__(self, client, project_key, notebook_name, content=None): + self.client = client + self.project_key = project_key + self.notebook_name = notebook_name + self.content = content + + def get_raw(self): + """ + Get the content of this Jupyter notebook (metadata, cells, nbformat) + :rtype: list with the full content of a notebook + """ + if self.content is None: + self.content = self.client._perform_json("GET", + "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) + return self.content + + def get_metadata(self): + """ + Get the metadata associated to this Jupyter notebook + :rtype: list with metadata + """ + return self.get_raw()["metadata"] + + def get_cells(self): + """ + Get the cells associated to this Jupyter notebook + :rtype: list of cells + """ + return self.get_raw()["cells"] + + + def save(self): + """ + Save the content of this Jupyter notebook + """ + if self.content is None: + raise ValueError("Notebook content is empty, use \"get_content()\" or manually set the content of the notebook before saving") + return self.client._perform_json("PUT", + "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name), + body=self.content) diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index e810fb6c..6793dc64 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -857,7 +857,7 @@ def get_jupyter_notebook(self, notebook_name): :param str notebook_name: The name of the jupyter notebook to retrieve :returns: A handle to interact with this jupyter notebook - :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebookContent` jupyter notebook handle + :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebook` jupyter notebook handle """ notebook_state = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name)) return DSSNotebook(self.client, self.project_key, notebook_name, state=notebook_state) From 2f0464b2911c5917a1af492e0e3d7b59b8eacc13 Mon Sep 17 00:00:00 2001 From: pbailly Date: Mon, 15 Mar 2021 19:03:45 +0100 Subject: [PATCH 06/11] Separe legacy API from the new API. --- dataikuapi/dss/jupyternootebook.py | 101 +++++++++++++++++++++++++++++ dataikuapi/dss/notebook.py | 76 ++++++++++------------ dataikuapi/dss/notebookcontent.py | 46 ------------- dataikuapi/dss/project.py | 27 ++++---- dataikuapi/dssclient.py | 10 +-- 5 files changed, 151 insertions(+), 109 deletions(-) create mode 100644 dataikuapi/dss/jupyternootebook.py delete mode 100644 dataikuapi/dss/notebookcontent.py diff --git a/dataikuapi/dss/jupyternootebook.py b/dataikuapi/dss/jupyternootebook.py new file mode 100644 index 00000000..76f111e0 --- /dev/null +++ b/dataikuapi/dss/jupyternootebook.py @@ -0,0 +1,101 @@ +from .discussion import DSSObjectDiscussions + +class DSSJupyterNotebook(object): + """ + A Python/R/Scala notebook on the DSS instance + """ + def __init__(self, client, project_key, notebook_name, content=None): + self.client = client + self.project_key = project_key + self.notebook_name = notebook_name + self.content = content + + def unload(self, session_id=None): + """ + Stop this Jupyter notebook and release its resources + """ + sessions = self.get_sessions() + if sessions is None: + raise Exception("Notebook isn't running") + if len(sessions) == 0: + raise Exception("Notebook isn't running") + if session_id is None: + if len(sessions) > 1: + raise Exception("Several sessions of the notebook are running, choose one") + else: + session_id = sessions[0].get('sessionId', None) + return self.client._perform_json("DELETE", + "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) + + def get_sessions(self): + """ + Get the list of running sessions of this Jupyter notebook + """ + sessions = self.client._perform_json("GET", + "/projects/%s/jupyter-notebooks/%s/sessions" % (self.project_key, self.notebook_name)) + return sessions + + def get_content(self): + """ + Get the content of this Jupyter notebook (metadata, cells, nbformat) + """ + if self.content is None: + raw_content = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) + self.content = DSSNotebookContent(self.client, self.project_key, self.notebook_name, raw_content) + return self.content + + def delete(self): + """ + Delete this Jupyter notebook and stop all of its active sessions. + """ + return self.client._perform_json("DELETE", + "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) + + ######################################################## + # Discussions + ######################################################## + def get_object_discussions(self): + """ + Get a handle to manage discussions on the notebook + + :returns: the handle to manage discussions + :rtype: :class:`dataikuapi.discussion.DSSObjectDiscussions` + """ + return DSSObjectDiscussions(self.client, self.project_key, "JUPYTER_NOTEBOOK", self.notebook_name) + +class DSSNotebookContent(object): + """ + Settings of a download recipe. Do not create this directly, use :meth:`DSSJupyterNotebook.get_content` + """ + + """ + A Python/R/Scala notebook on the DSS instance + """ + def __init__(self, client, project_key, notebook_name, content=None): + self.client = client + self.project_key = project_key + self.notebook_name = notebook_name + self.content = content + + def get_raw(self): + """ + Get the content of this Jupyter notebook (metadata, cells, nbformat) + :rtype: a list containing the full content of a notebook + """ + return self.content + + def get_cells(self): + """ + Get the cells associated to this Jupyter notebook + :rtype: list of cells + """ + return self.content["cells"] + + + def save(self): + """ + Save the content of this Jupyter notebook + """ + return self.client._perform_json("PUT", + "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name), + body=self.content) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index d9cc4bd0..8ec867a4 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -1,81 +1,73 @@ from .discussion import DSSObjectDiscussions -from .notebookcontent import DSSNotebookContent +import warnings class DSSNotebook(object): """ + Deprecated. Use DSSJupyterNotebook A Python/R/Scala notebook on the DSS instance """ - def __init__(self, client, project_key, notebook_name, state=None, content=None): - self.client = client - self.project_key = project_key - self.notebook_name = notebook_name - self.state = state - if content: - self.content = content - else: - self.content = DSSNotebookContent(self.client, self.project_key, self.notebook_name) + def __init__(self, client, project_key, notebook_name, state=None): + warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) + self.client = client + self.project_key = project_key + self.notebook_name = notebook_name + self.state = state + self.state_is_peek = True def unload(self, session_id=None): """ + Deprecated. Use DSSJupyterNotebook Stop this Jupyter notebook and release its resources """ - sessions = self.get_sessions() - if sessions is None: + warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) + state = self.get_state() + if state is None: raise Exception("Notebook isn't running") - if len(sessions) == 0: + if state.get('activeSessions', None) is None: + raise Exception("Notebook isn't running") + if len(state['activeSessions']) == 0: raise Exception("Notebook isn't running") if session_id is None: - if len(sessions) > 1: + if len(state['activeSessions']) > 1: raise Exception("Several sessions of the notebook are running, choose one") else: - session_id = sessions[0].get('sessionId', None) - return self.client._perform_json("DELETE", - "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) + session_id = state['activeSessions'][0].get('sessionId', None) + return self.client._perform_json("DELETE", "/projects/%s/notebooks/" % self.project_key, params={'notebookName' : self.notebook_name, 'sessionId' : session_id}) + def get_state(self): """ - Deprecated. Use self.get_content().get_metadata() + Deprecated. Use DSSJupyterNotebook Get the metadata associated to this Jupyter notebook """ - import warnings - warnings.warn("Use self.get_content().get_metadata()", DeprecationWarning) - self.state = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, self.notebook_name)) + warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) + if self.state is None: + self.state = self.client._perform_json("GET", "/projects/%s/notebooks/" % self.project_key, params={'notebookName' : self.notebook_name}) return self.state def get_sessions(self): """ + Deprecated. Use DSSJupyterNotebook Get the list of running sessions of this Jupyter notebook """ - - if self.state is None: - self.state = {} - sessions = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/%s/sessions" % (self.project_key, self.notebook_name)) - self.state["activeSessions"] = sessions - return sessions - - def get_content(self): - """ - Get the content of this Jupyter notebook (metadata, cells, nbformat) - """ - return self.content - - def delete(self): - """ - Delete this Jupyter notebook and stop all of its active sessions. - """ - return self.client._perform_json("DELETE", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) + warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) + state = self.get_state() + if state is None: + raise Exception("Notebook isn't running") + if state.get('activeSessions', None) is None: + raise Exception("Notebook isn't running") + return state['activeSessions'] ######################################################## # Discussions ######################################################## def get_object_discussions(self): """ + Deprecated. Use DSSJupyterNotebook Get a handle to manage discussions on the notebook :returns: the handle to manage discussions :rtype: :class:`dataikuapi.discussion.DSSObjectDiscussions` """ + warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) return DSSObjectDiscussions(self.client, self.project_key, "JUPYTER_NOTEBOOK", self.notebook_name) diff --git a/dataikuapi/dss/notebookcontent.py b/dataikuapi/dss/notebookcontent.py deleted file mode 100644 index dff3e423..00000000 --- a/dataikuapi/dss/notebookcontent.py +++ /dev/null @@ -1,46 +0,0 @@ -from .discussion import DSSObjectDiscussions - -class DSSNotebookContent(object): - """ - A Python/R/Scala notebook on the DSS instance - """ - def __init__(self, client, project_key, notebook_name, content=None): - self.client = client - self.project_key = project_key - self.notebook_name = notebook_name - self.content = content - - def get_raw(self): - """ - Get the content of this Jupyter notebook (metadata, cells, nbformat) - :rtype: list with the full content of a notebook - """ - if self.content is None: - self.content = self.client._perform_json("GET", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) - return self.content - - def get_metadata(self): - """ - Get the metadata associated to this Jupyter notebook - :rtype: list with metadata - """ - return self.get_raw()["metadata"] - - def get_cells(self): - """ - Get the cells associated to this Jupyter notebook - :rtype: list of cells - """ - return self.get_raw()["cells"] - - - def save(self): - """ - Save the content of this Jupyter notebook - """ - if self.content is None: - raise ValueError("Notebook content is empty, use \"get_content()\" or manually set the content of the notebook before saving") - return self.client._perform_json("PUT", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name), - body=self.content) diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index 6793dc64..a27637b8 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -1,6 +1,6 @@ import time, warnings, sys, os.path as osp from .dataset import DSSDataset, DSSDatasetListItem, DSSManagedDatasetCreationHelper -from .notebookcontent import DSSNotebookContent +from .jupyternootebook import DSSJupyterNotebook, DSSNotebookContent from .streaming_endpoint import DSSStreamingEndpoint, DSSStreamingEndpointListItem, DSSManagedStreamingEndpointCreationHelper from .recipe import DSSRecipeListItem, DSSRecipe from . import recipe @@ -12,7 +12,6 @@ from .continuousactivity import DSSContinuousActivity from .apiservice import DSSAPIService from .future import DSSFuture -from .notebook import DSSNotebook from .macro import DSSMacro from .wiki import DSSWiki from .discussion import DSSObjectDiscussions @@ -20,7 +19,6 @@ from .analysis import DSSAnalysis from .flow import DSSProjectFlow from .app import DSSAppManifest -from ..utils import DataikuException class DSSProject(object): @@ -842,14 +840,11 @@ def list_jupyter_notebooks(self, as_objects=True, active=False): :returns: The list of the notebooks - see as_objects for more information :rtype: list """ - notebook_names = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/" % self.project_key) - notebooks = [] - for notebook_name in notebook_names: - notebooks.append(self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name))) + notebook_names = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/" % self.project_key, params={"active": active}) if as_objects: - return [DSSNotebook(self.client, notebook_state['projectKey'], notebook_state['name'], state=notebook_state) for notebook_state in notebooks] + return [DSSJupyterNotebook(self.client, self.project_key, notebook_name) for notebook_name in notebook_names] else: - return notebooks + return notebook_names def get_jupyter_notebook(self, notebook_name): """ @@ -859,8 +854,10 @@ def get_jupyter_notebook(self, notebook_name): :returns: A handle to interact with this jupyter notebook :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebook` jupyter notebook handle """ - notebook_state = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name)) - return DSSNotebook(self.client, self.project_key, notebook_name, state=notebook_state) + notebook = DSSJupyterNotebook(self.client, self.project_key, notebook_name) + # Force content download + notebook.get_content() + return notebook def create_jupyter_notebook(self, notebook_name, notebook_content): """ @@ -876,10 +873,8 @@ def create_jupyter_notebook(self, notebook_name, notebook_content): created_notebook_content = self.client._perform_json("POST", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, notebook_name), body=notebook_content) - # Retrieve back the metadata after creation - notebook_state = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/metadata" % (self.project_key, notebook_name)) - return DSSNotebook(self.client, self.project_key, notebook_name, state=notebook_state, - content=DSSNotebookContent(self.client, self.project_key, notebook_name, created_notebook_content)) + return DSSJupyterNotebook(self.client, self.project_key, notebook_name, + content=DSSNotebookContent(self.client, self.project_key, notebook_name, created_notebook_content)) ######################################################## # Continuous activities @@ -1288,7 +1283,7 @@ def list_running_notebooks(self, as_objects=True): """ list = self.client._perform_json("GET", "/projects/%s/notebooks/active" % self.project_key) if as_objects: - return [DSSNotebook(self.client, notebook['projectKey'], notebook['name'], notebook) for notebook in list] + return [DSSJupyterNotebook(self.client, notebook['projectKey'], notebook['name'], notebook) for notebook in list] else: return list diff --git a/dataikuapi/dssclient.py b/dataikuapi/dssclient.py index 8a5cb2f8..af8671ad 100644 --- a/dataikuapi/dssclient.py +++ b/dataikuapi/dssclient.py @@ -3,6 +3,7 @@ from requests import exceptions from requests.auth import HTTPBasicAuth +from dataikuapi.dss.jupyternootebook import DSSJupyterNotebook from .dss.future import DSSFuture from .dss.projectfolder import DSSProjectFolder from .dss.project import DSSProject @@ -11,7 +12,6 @@ from .dss.admin import DSSUser, DSSOwnUser, DSSGroup, DSSConnection, DSSGeneralSettings, DSSCodeEnv, DSSGlobalApiKey, DSSCluster from .dss.meaning import DSSMeaning from .dss.sqlquery import DSSSQLQuery -from .dss.notebook import DSSNotebook from .dss.discussion import DSSObjectDiscussions from .dss.apideployer import DSSAPIDeployer from .dss.projectdeployer import DSSProjectDeployer @@ -95,14 +95,14 @@ def list_running_notebooks(self, as_objects=True): """ List the currently-running Jupyter notebooks - :param boolean as_objects: if True, each returned item will be a :class:`dataikuapi.dss.notebook.DSSNotebook` + :param boolean as_objects: if True, each returned item will be a :class:`dataikuapi.dss.notebook.DSSJupyterNotebook` - :return: list of notebooks. if as_objects is True, each entry in the list is a :class:`dataikuapi.dss.notebook.DSSNotebook`. Else, each item in the list is a dict which contains at least a "name" field. - :rtype: list of :class:`dataikuapi.dss.notebook.DSSNotebook` or list of dict + :return: list of notebooks. if as_objects is True, each entry in the list is a :class:`dataikuapi.dss.notebook.DSSJupyterNotebook`. Else, each item in the list is a dict which contains at least a "name" field. + :rtype: list of :class:`dataikuapi.dss.notebook.DSSJupyterNotebook` or list of dict """ list = self._perform_json("GET", "/admin/notebooks/") if as_objects: - return [DSSNotebook(self, notebook['projectKey'], notebook['name'], notebook) for notebook in list] + return [DSSJupyterNotebook(self, notebook['projectKey'], notebook['name'], notebook) for notebook in list] else: return list From e6d6aaeb8438fd953734ffc21429130bef86c655 Mon Sep 17 00:00:00 2001 From: pbailly Date: Wed, 17 Mar 2021 19:12:28 +0100 Subject: [PATCH 07/11] Improve code consistency. --- dataikuapi/dss/jupyternootebook.py | 22 ++++++++++------- dataikuapi/dss/project.py | 17 ++++++------- dataikuapi/dssclient.py | 39 +++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/dataikuapi/dss/jupyternootebook.py b/dataikuapi/dss/jupyternootebook.py index 76f111e0..08b4c067 100644 --- a/dataikuapi/dss/jupyternootebook.py +++ b/dataikuapi/dss/jupyternootebook.py @@ -4,11 +4,10 @@ class DSSJupyterNotebook(object): """ A Python/R/Scala notebook on the DSS instance """ - def __init__(self, client, project_key, notebook_name, content=None): + def __init__(self, client, project_key, notebook_name): self.client = client self.project_key = project_key self.notebook_name = notebook_name - self.content = content def unload(self, session_id=None): """ @@ -39,10 +38,8 @@ def get_content(self): """ Get the content of this Jupyter notebook (metadata, cells, nbformat) """ - if self.content is None: - raw_content = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) - self.content = DSSNotebookContent(self.client, self.project_key, self.notebook_name, raw_content) - return self.content + raw_content = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name)) + return DSSNotebookContent(self.client, self.project_key, self.notebook_name, raw_content) def delete(self): """ @@ -65,13 +62,13 @@ def get_object_discussions(self): class DSSNotebookContent(object): """ - Settings of a download recipe. Do not create this directly, use :meth:`DSSJupyterNotebook.get_content` + Content of a Jupyter Notebook. Do not create this directly, use :meth:`DSSJupyterNotebook.get_content` """ """ A Python/R/Scala notebook on the DSS instance """ - def __init__(self, client, project_key, notebook_name, content=None): + def __init__(self, client, project_key, notebook_name, content): self.client = client self.project_key = project_key self.notebook_name = notebook_name @@ -80,10 +77,17 @@ def __init__(self, client, project_key, notebook_name, content=None): def get_raw(self): """ Get the content of this Jupyter notebook (metadata, cells, nbformat) - :rtype: a list containing the full content of a notebook + :rtype: a dict containing the full content of a notebook """ return self.content + def get_metadata(self): + """ + Get the metadata associated to this Jupyter notebook + :rtype: dict with metadata + """ + return self.content["metadata"] + def get_cells(self): """ Get the cells associated to this Jupyter notebook diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index a27637b8..f00f3246 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -1,6 +1,7 @@ import time, warnings, sys, os.path as osp from .dataset import DSSDataset, DSSDatasetListItem, DSSManagedDatasetCreationHelper from .jupyternootebook import DSSJupyterNotebook, DSSNotebookContent +from .notebook import DSSNotebook from .streaming_endpoint import DSSStreamingEndpoint, DSSStreamingEndpointListItem, DSSManagedStreamingEndpointCreationHelper from .recipe import DSSRecipeListItem, DSSRecipe from . import recipe @@ -870,11 +871,8 @@ def create_jupyter_notebook(self, notebook_name, notebook_content): :returns: A handle to interact with the newly created jupyter notebook :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebook` jupyter notebook handle """ - created_notebook_content = self.client._perform_json("POST", - "/projects/%s/jupyter-notebooks/%s" % (self.project_key, notebook_name), - body=notebook_content) - return DSSJupyterNotebook(self.client, self.project_key, notebook_name, - content=DSSNotebookContent(self.client, self.project_key, notebook_name, created_notebook_content)) + self.client._perform_json("POST", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, notebook_name), body=notebook_content) + return self.get_jupyter_notebook(notebook_name) ######################################################## # Continuous activities @@ -1273,19 +1271,20 @@ def sync_datasets_acls(self): ######################################################## # Notebooks ######################################################## - + def list_running_notebooks(self, as_objects=True): """ + Deprecated. Use :meth:`DSSProject.list_jupyter_notebooks` List the currently-running notebooks Returns: list of notebooks. Each object contains at least a 'name' field """ - list = self.client._perform_json("GET", "/projects/%s/notebooks/active" % self.project_key) + notebook_list = self.client._perform_json("GET", "/projects/%s/notebooks/active" % self.project_key) if as_objects: - return [DSSJupyterNotebook(self.client, notebook['projectKey'], notebook['name'], notebook) for notebook in list] + return [DSSNotebook(self.client, notebook['projectKey'], notebook['name'], notebook) for notebook in notebook_list] else: - return list + return notebook_list ######################################################## # Tags diff --git a/dataikuapi/dssclient.py b/dataikuapi/dssclient.py index af8671ad..374492c2 100644 --- a/dataikuapi/dssclient.py +++ b/dataikuapi/dssclient.py @@ -1,9 +1,12 @@ import json +import warnings + from requests import Session from requests import exceptions from requests.auth import HTTPBasicAuth from dataikuapi.dss.jupyternootebook import DSSJupyterNotebook +from dataikuapi.dss.notebook import DSSNotebook from .dss.future import DSSFuture from .dss.projectfolder import DSSProjectFolder from .dss.project import DSSProject @@ -90,21 +93,45 @@ def get_future(self, job_id): ######################################################## # Notebooks ######################################################## - - def list_running_notebooks(self, as_objects=True): + + + def list_jupyter_notebooks(self, active=False, as_objects=True): """ - List the currently-running Jupyter notebooks + List the Jupyter notebooks for every projects + :param boolean active: if True, only the active notebooks :param boolean as_objects: if True, each returned item will be a :class:`dataikuapi.dss.notebook.DSSJupyterNotebook` :return: list of notebooks. if as_objects is True, each entry in the list is a :class:`dataikuapi.dss.notebook.DSSJupyterNotebook`. Else, each item in the list is a dict which contains at least a "name" field. :rtype: list of :class:`dataikuapi.dss.notebook.DSSJupyterNotebook` or list of dict """ - list = self._perform_json("GET", "/admin/notebooks/") + project_keys = self.list_project_keys() + output = [] + for project_key in project_keys: + notebook_names = self._perform_json("GET", "/projects/%s/jupyter-notebooks/" % project_key, params={"active": active}) + if as_objects: + output += [DSSJupyterNotebook(self, project_key, notebook_name) for notebook_name in notebook_names] + else: + for notebook_name in notebook_names: + output.append({u"projectKey": project_key, u"name": notebook_name}) + return output + + def list_running_notebooks(self, as_objects=True): + """ + Deprecated. Use :meth:`DSSClient.list_jupyter_notebooks` + List the currently-running Jupyter notebooks + + :param boolean as_objects: if True, each returned item will be a :class:`dataikuapi.dss.notebook.DSSNotebook` + + :return: list of notebooks. if as_objects is True, each entry in the list is a :class:`dataikuapi.dss.notebook.DSSNotebook`. Else, each item in the list is a dict which contains at least a "name" field. + :rtype: list of :class:`dataikuapi.dss.notebook.DSSNotebook` or list of dict + """ + warnings.warn("Use DSSClient.list_jupyter_notebooks", DeprecationWarning) + notebook_list = self._perform_json("GET", "/admin/notebooks/") if as_objects: - return [DSSJupyterNotebook(self, notebook['projectKey'], notebook['name'], notebook) for notebook in list] + return [DSSNotebook(self, notebook['projectKey'], notebook['name'], notebook) for notebook in notebook_list] else: - return list + return notebook_list ######################################################## # Project folders From d76048e08e3f6bd57a51f8d5795dc08f1321f34c Mon Sep 17 00:00:00 2001 From: pbailly Date: Mon, 22 Mar 2021 18:03:14 +0100 Subject: [PATCH 08/11] Improve session handling. --- dataikuapi/dss/jupyternootebook.py | 43 +++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/dataikuapi/dss/jupyternootebook.py b/dataikuapi/dss/jupyternootebook.py index 08b4c067..ee6f4e95 100644 --- a/dataikuapi/dss/jupyternootebook.py +++ b/dataikuapi/dss/jupyternootebook.py @@ -1,9 +1,6 @@ from .discussion import DSSObjectDiscussions class DSSJupyterNotebook(object): - """ - A Python/R/Scala notebook on the DSS instance - """ def __init__(self, client, project_key, notebook_name): self.client = client self.project_key = project_key @@ -26,13 +23,20 @@ def unload(self, session_id=None): return self.client._perform_json("DELETE", "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, session_id)) - def get_sessions(self): + def get_sessions(self, as_objects=False): """ Get the list of running sessions of this Jupyter notebook + + :param boolean as_objects: if True, each returned item will be a :class:`dataikuapi.dss.notebook.DSSNotebookSession` + :rtype: list of :class:`dataikuapi.dss.notebook.DSSNotebookSession` or list of dict """ sessions = self.client._perform_json("GET", "/projects/%s/jupyter-notebooks/%s/sessions" % (self.project_key, self.notebook_name)) - return sessions + + if as_objects: + return [DSSNotebookSession(self.client, session) for session in sessions] + else: + return sessions def get_content(self): """ @@ -95,7 +99,6 @@ def get_cells(self): """ return self.content["cells"] - def save(self): """ Save the content of this Jupyter notebook @@ -103,3 +106,31 @@ def save(self): return self.client._perform_json("PUT", "/projects/%s/jupyter-notebooks/%s" % (self.project_key, self.notebook_name), body=self.content) + +class DSSNotebookSession(object): + """ + Metadata associated to the session of a Jupyter Notebook. Do not create this directly, use :meth:`DSSJupyterNotebook.get_sessions()` + """ + + def __init__(self, client, session): + self.client = client + self.project_key = session.get("projectKey") + self.notebook_name = session.get("notebookName") + self.session_creator = session.get("sessionCreator") + self.session_creator_display_name = session.get("sessionCreatorDisplayName") + self.session_unix_owner = session.get("sessionUnixOwner") + self.session_id = session.get("sessionId") + self.kernel_id = session.get("kernelId") + self.kernel_pid = session.get("kernelPid") + self.kernel_connections = session.get("kernelConnections") + self.kernel_last_activity_time = session.get("kernelLastActivityTime") + self.kernel_execution_state = session.get("kernelExecutionState") + self.session_start_time = session.get("sessionStartTime") + + + def unload(self): + """ + Stop this Jupyter notebook and release its resources + """ + return self.client._perform_json("DELETE", + "/projects/%s/jupyter-notebooks/%s/sessions/%s" % (self.project_key, self.notebook_name, self.session_id)) From cf8a93e16b48c9ab8bc19a4d14f6d2d0d34840d8 Mon Sep 17 00:00:00 2001 From: pbailly Date: Tue, 23 Mar 2021 14:36:16 +0100 Subject: [PATCH 09/11] Tweak after code review. --- dataikuapi/dss/notebook.py | 1 - dataikuapi/dss/project.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index 8ec867a4..c9a71b9c 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -7,7 +7,6 @@ class DSSNotebook(object): A Python/R/Scala notebook on the DSS instance """ def __init__(self, client, project_key, notebook_name, state=None): - warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) self.client = client self.project_key = project_key self.notebook_name = notebook_name diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index f00f3246..848ac5f7 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -855,10 +855,7 @@ def get_jupyter_notebook(self, notebook_name): :returns: A handle to interact with this jupyter notebook :rtype: :class:`~dataikuapi.dss.notebook.DSSNotebook` jupyter notebook handle """ - notebook = DSSJupyterNotebook(self.client, self.project_key, notebook_name) - # Force content download - notebook.get_content() - return notebook + return DSSJupyterNotebook(self.client, self.project_key, notebook_name) def create_jupyter_notebook(self, notebook_name, notebook_content): """ From cce9e915d9ab59588837ea7ab845434edbf34b7a Mon Sep 17 00:00:00 2001 From: pbailly Date: Tue, 23 Mar 2021 15:43:10 +0100 Subject: [PATCH 10/11] Remove warnings. --- dataikuapi/dss/notebook.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dataikuapi/dss/notebook.py b/dataikuapi/dss/notebook.py index c9a71b9c..ad435039 100644 --- a/dataikuapi/dss/notebook.py +++ b/dataikuapi/dss/notebook.py @@ -1,5 +1,4 @@ from .discussion import DSSObjectDiscussions -import warnings class DSSNotebook(object): """ @@ -18,7 +17,6 @@ def unload(self, session_id=None): Deprecated. Use DSSJupyterNotebook Stop this Jupyter notebook and release its resources """ - warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) state = self.get_state() if state is None: raise Exception("Notebook isn't running") @@ -39,7 +37,6 @@ def get_state(self): Deprecated. Use DSSJupyterNotebook Get the metadata associated to this Jupyter notebook """ - warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) if self.state is None: self.state = self.client._perform_json("GET", "/projects/%s/notebooks/" % self.project_key, params={'notebookName' : self.notebook_name}) return self.state @@ -49,7 +46,6 @@ def get_sessions(self): Deprecated. Use DSSJupyterNotebook Get the list of running sessions of this Jupyter notebook """ - warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) state = self.get_state() if state is None: raise Exception("Notebook isn't running") @@ -68,5 +64,4 @@ def get_object_discussions(self): :returns: the handle to manage discussions :rtype: :class:`dataikuapi.discussion.DSSObjectDiscussions` """ - warnings.warn("Use DSSJupyterNotebook", DeprecationWarning) return DSSObjectDiscussions(self.client, self.project_key, "JUPYTER_NOTEBOOK", self.notebook_name) From 15e0e5782379456bfb37f7436c407d906f1d9deb Mon Sep 17 00:00:00 2001 From: Sourcery AI <> Date: Tue, 23 Mar 2021 14:43:53 +0000 Subject: [PATCH 11/11] 'Refactored by Sourcery' --- dataikuapi/dss/project.py | 33 ++++++++++++++++----------------- dataikuapi/dssclient.py | 12 +++++++----- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/dataikuapi/dss/project.py b/dataikuapi/dss/project.py index 848ac5f7..efbcf3a6 100644 --- a/dataikuapi/dss/project.py +++ b/dataikuapi/dss/project.py @@ -193,8 +193,9 @@ def duplicate(self, target_project_key, if target_project_folder is not None: obj["targetProjectFolderId"] = target_project_folder.project_folder_id - ref = self.client._perform_json("POST", "/projects/%s/duplicate/" % self.project_key, body = obj) - return ref + return self.client._perform_json( + "POST", "/projects/%s/duplicate/" % self.project_key, body=obj + ) ######################################################## # Project infos @@ -263,9 +264,9 @@ def list_datasets(self, as_type="listitems"): :rtype: list """ items = self.client._perform_json("GET", "/projects/%s/datasets/" % self.project_key) - if as_type == "listitems" or as_type == "listitem": + if as_type in ["listitems", "listitem"]: return [DSSDatasetListItem(self.client, item) for item in items] - elif as_type == "objects" or as_type == "object": + elif as_type in ["objects", "object"]: return [DSSDataset(self.client, self.project_key, item["name"]) for item in items] else: raise ValueError("Unknown as_type") @@ -403,9 +404,9 @@ def list_streaming_endpoints(self, as_type="listitems"): :rtype: list """ items = self.client._perform_json("GET", "/projects/%s/streamingendpoints/" % self.project_key) - if as_type == "listitems" or as_type == "listitem": + if as_type in ["listitems", "listitem"]: return [DSSStreamingEndpointListItem(self.client, item) for item in items] - elif as_type == "objects" or as_type == "object": + elif as_type in ["objects", "object"]: return [DSSStreamingEndpoint(self.client, self.project_key, item["id"]) for item in items] else: raise ValueError("Unknown as_type") @@ -715,7 +716,7 @@ def list_model_evaluation_stores(self, as_type=None): :rtype: list """ items = self.client._perform_json("GET", "/projects/%s/modelevaluationstores/" % self.project_key) - if as_type == "objects" or as_type == "object": + if as_type in ["objects", "object"]: return [DSSModelEvaluationStore(self.client, self.project_key, item["id"]) for item in items] else: return items @@ -921,9 +922,9 @@ def set_variables(self, obj): @param dict obj: must be a modified version of the object returned by get_variables """ - if not "standard" in obj: + if "standard" not in obj: raise ValueError("Missing 'standard' key in argument") - if not "local" in obj: + if "local" not in obj: raise ValueError("Missing 'local' key in argument") self.client._perform_empty( @@ -1140,9 +1141,9 @@ def list_recipes(self, as_type="listitems"): :rtype: list """ items = self.client._perform_json("GET", "/projects/%s/recipes/" % self.project_key) - if as_type == "listitems" or as_type == "listitem": + if as_type in ["listitems", "listitem"]: return [DSSRecipeListItem(self.client, item) for item in items] - elif as_type == "objects" or as_type == "object": + elif as_type in ["objects", "object"]: return [DSSRecipe(self.client, self.project_key, item["name"]) for item in items] else: raise ValueError("Unknown as_type") @@ -1228,7 +1229,7 @@ def new_recipe(self, type, name=None): return recipe.SamplingRecipeCreator(name, self) elif type == "split": return recipe.SplitRecipeCreator(name, self) - elif type == "prepare" or type == "shaker": + elif type in ["prepare", "shaker"]: return recipe.PrepareRecipeCreator(name, self) elif type == "prediction_scoring": return recipe.PredictionScoringRecipeCreator(name, self) @@ -1587,11 +1588,9 @@ def add_exposed_object(self, object_type, object_id, target_project): found_eo = {"type" : object_type, "localName" : object_id, "rules" : []} self.settings["exposedObjects"]["objects"].append(found_eo) - already_exists = False - for rule in found_eo["rules"]: - if rule["targetProject"] == target_project: - already_exists = True - break + already_exists = any( + rule["targetProject"] == target_project for rule in found_eo["rules"] + ) if not already_exists: found_eo["rules"].append({"targetProject": target_project}) diff --git a/dataikuapi/dssclient.py b/dataikuapi/dssclient.py index 374492c2..8296ecf1 100644 --- a/dataikuapi/dssclient.py +++ b/dataikuapi/dssclient.py @@ -591,10 +591,12 @@ def create_cluster(self, cluster_name, cluster_type='manual', params=None): :returns: A :class:`dataikuapi.dss.admin.DSSCluster` cluster handle """ - definition = {} - definition['name'] = cluster_name - definition['type'] = cluster_type - definition['params'] = params if params is not None else {} + definition = { + 'name': cluster_name, + 'type': cluster_type, + 'params': params if params is not None else {}, + } + resp = self._perform_json( "POST", "/admin/clusters/", body=definition) if resp is None: @@ -705,7 +707,7 @@ def create_meaning(self, id, label, type, description=None, :returns: A :class:`dataikuapi.dss.meaning.DSSMeaning` meaning handle """ def make_entry(v): - if isinstance(v, str) or isinstance(v, unicode): + if isinstance(v, (str, unicode)): return {'value':v} else: return v