From bd33fc1ada5fe28cc15318ec0e56eb1bd6bfd31d Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 07:33:00 +0100 Subject: [PATCH 01/39] adding .vscode to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c06e715ef..060db33be 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ target/ # IDE .idea *.swp +.vscode # MYPY .mypy_cache From 4145ff94667b750e6d00ca4525066d9334f5c93a Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 07:35:58 +0100 Subject: [PATCH 02/39] Adding delete_dataset() to datasets/functions.py --- openml/datasets/functions.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openml/datasets/functions.py b/openml/datasets/functions.py index 770413a23..99678e21f 100644 --- a/openml/datasets/functions.py +++ b/openml/datasets/functions.py @@ -1271,3 +1271,18 @@ def _get_online_dataset_format(dataset_id): dataset_xml = openml._api_calls._perform_api_call("data/%d" % dataset_id, "get") # build a dict from the xml and get the format from the dataset description return xmltodict.parse(dataset_xml)["oml:data_set_description"]["oml:format"].lower() + +def delete_dataset(dataset_id: int) -> bool: + """Deletes a dataset from the OpenML server. + + Parameters + ---------- + dataset_id : int + OpenML id of the dataset + + Returns + ------- + bool + True if the deletion was successful. False otherwise + """ + return openml.utils._delete_entity("dataset", dataset_id) From 4320c89f9d1cb0c2461964d16f0beb52e0db016b Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 07:40:05 +0100 Subject: [PATCH 03/39] Adding delete_dataset() to datasets/__init__.py --- openml/datasets/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openml/datasets/__init__.py b/openml/datasets/__init__.py index abde85c06..efa5a5d5b 100644 --- a/openml/datasets/__init__.py +++ b/openml/datasets/__init__.py @@ -11,6 +11,7 @@ list_qualities, edit_dataset, fork_dataset, + delete_dataset, ) from .dataset import OpenMLDataset from .data_feature import OpenMLDataFeature @@ -28,4 +29,5 @@ "list_qualities", "edit_dataset", "fork_dataset", + "delete_dataset", ] From 06e4a0327b0d6e0d1f449bac0a9b4e31ad90a145 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:00:05 +0100 Subject: [PATCH 04/39] Adding delete_flow() to flows/functions.py --- openml/flows/functions.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openml/flows/functions.py b/openml/flows/functions.py index 43cb453fa..ac4cd9115 100644 --- a/openml/flows/functions.py +++ b/openml/flows/functions.py @@ -547,3 +547,19 @@ def _create_flow_from_xml(flow_xml: str) -> OpenMLFlow: """ return OpenMLFlow._from_dict(xmltodict.parse(flow_xml)) + +def delete_flow(flow_id: int) -> bool: + """Deletes a dataset from the OpenML server. + + Parameters + ---------- + flow_id : int + OpenML id of the flow + + Returns + ------- + bool + True if the deletion was successful. False otherwise + """ + return openml.utils._delete_entity("flow", flow_id) + \ No newline at end of file From 5ad6af62aa104f2644406d81b8370de1a0ef9589 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:02:40 +0100 Subject: [PATCH 05/39] Adding delete_flow to flows/__init__.py --- openml/flows/__init__.py | 10 +++++++++- openml/flows/functions.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/openml/flows/__init__.py b/openml/flows/__init__.py index 3642b9c56..dd1bf82ca 100644 --- a/openml/flows/__init__.py +++ b/openml/flows/__init__.py @@ -2,7 +2,14 @@ from .flow import OpenMLFlow -from .functions import get_flow, list_flows, flow_exists, get_flow_id, assert_flows_equal +from .functions import ( + get_flow, + list_flows, + flow_exists, + get_flow_id, + assert_flows_equal, + delete_flow, +) __all__ = [ "OpenMLFlow", @@ -11,4 +18,5 @@ "get_flow_id", "flow_exists", "assert_flows_equal", + "delete_flow", ] diff --git a/openml/flows/functions.py b/openml/flows/functions.py index ac4cd9115..00f759187 100644 --- a/openml/flows/functions.py +++ b/openml/flows/functions.py @@ -549,7 +549,7 @@ def _create_flow_from_xml(flow_xml: str) -> OpenMLFlow: return OpenMLFlow._from_dict(xmltodict.parse(flow_xml)) def delete_flow(flow_id: int) -> bool: - """Deletes a dataset from the OpenML server. + """Deletes a flow from the OpenML server. Parameters ---------- From 83b1bf1e8110e4eca2fcfa194f17b76b8657e2f6 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:06:51 +0100 Subject: [PATCH 06/39] Adding delete_task() to task/functions.py --- openml/tasks/functions.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index c44d55ea7..1363ad52b 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -545,3 +545,19 @@ def create_task( evaluation_measure=evaluation_measure, **kwargs, ) + +def delete_task(task_id: int) -> bool: + """Deletes a task from the OpenML server. + + Parameters + ---------- + task_id : int + OpenML id of the task + + Returns + ------- + bool + True if the deletion was successful. False otherwise + """ + return openml.utils._delete_entity("task", task_id) + \ No newline at end of file From 9567927699155a51881b7bf974c9f8e87df266f8 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:07:54 +0100 Subject: [PATCH 07/39] Adding delete_task() to tasks/__init__.py --- openml/tasks/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openml/tasks/__init__.py b/openml/tasks/__init__.py index cba0aa14f..a5d578d2d 100644 --- a/openml/tasks/__init__.py +++ b/openml/tasks/__init__.py @@ -15,6 +15,7 @@ get_task, get_tasks, list_tasks, + delete_task, ) __all__ = [ @@ -30,4 +31,5 @@ "list_tasks", "OpenMLSplit", "TaskType", + "delete_task", ] From f8c0f7dacc87395caf7b411f10dc565106f6fd25 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:10:02 +0100 Subject: [PATCH 08/39] Adding delete_run() to runs/functions.py --- openml/runs/functions.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openml/runs/functions.py b/openml/runs/functions.py index 08b2fe972..145e07c76 100644 --- a/openml/runs/functions.py +++ b/openml/runs/functions.py @@ -1205,3 +1205,18 @@ def format_prediction( return [repeat, fold, index, truth, prediction] else: raise NotImplementedError(f"Formatting for {type(task)} is not supported.") + +def delete_run(run_id: int) -> bool: + """Deletes a run from the OpenML server. + + Parameters + ---------- + run_id : int + OpenML id of the run + + Returns + ------- + bool + True if the deletion was successful. False otherwise + """ + return openml.utils._delete_entity("run", run_id) From 9308a5e4b538bb13d3ee38bbf3746376a43aeba7 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:10:47 +0100 Subject: [PATCH 09/39] Adding delete_run() to runs/__init__.py --- openml/runs/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openml/runs/__init__.py b/openml/runs/__init__.py index e917a57a5..2abbd8f29 100644 --- a/openml/runs/__init__.py +++ b/openml/runs/__init__.py @@ -12,6 +12,7 @@ run_exists, initialize_model_from_run, initialize_model_from_trace, + delete_run, ) __all__ = [ @@ -27,4 +28,5 @@ "run_exists", "initialize_model_from_run", "initialize_model_from_trace", + "delete_run", ] From a3b1688bdcc7fc67281526660bad66b5e86efb41 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Thu, 1 Apr 2021 12:44:17 +0200 Subject: [PATCH 10/39] Adding 'test_delete_run()' to the unit test --- tests/test_runs/test_run_functions.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/test_runs/test_run_functions.py b/tests/test_runs/test_run_functions.py index 1e92613c3..c60e42d7f 100644 --- a/tests/test_runs/test_run_functions.py +++ b/tests/test_runs/test_run_functions.py @@ -24,7 +24,7 @@ import openml.extensions.sklearn from openml.testing import TestBase, SimpleImputer, CustomImputer from openml.extensions.sklearn import cat, cont -from openml.runs.functions import _run_task_get_arffcontent, run_exists, format_prediction +from openml.runs.functions import _run_task_get_arffcontent, run_exists, format_prediction, delete_run from openml.runs.trace import OpenMLRunTrace from openml.tasks import TaskType from openml.testing import check_task_existence @@ -1741,3 +1741,28 @@ def test_joblib_backends(self, parallel_mock): self.assertEqual(len(res[2]["predictive_accuracy"][0]), 10) self.assertEqual(len(res[3]["predictive_accuracy"][0]), 10) self.assertEqual(parallel_mock.call_count, call_count) + + @unittest.skipIf( + LooseVersion(sklearn.__version__) < "0.20", + reason="SimpleImputer doesn't handle mixed type DataFrame as input", + ) + def test_delete_run(self): + rs = 1 + clf = sklearn.pipeline.Pipeline( + steps=[ + ('imputer', SimpleImputer()), + ('estimator', DecisionTreeClassifier()), + ] + ) + task = openml.tasks.get_task(32) # diabetes; crossvalidation + + run = openml.runs.run_model_on_task( + model=clf, task=task, seed=rs + ) + run.publish() + TestBase._mark_entity_for_removal("run", run.run_id) + TestBase.logger.info("collected from test_run_functions: {}".format(run.run_id)) + + + _run_id = run.run_id + self.assertTrue(delete_run(_run_id)) From 33f0f3743a8d104b5ed3eefcaf2f249752786aa8 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Thu, 1 Apr 2021 14:37:06 +0200 Subject: [PATCH 11/39] adding test_delete_flow to the unit tests --- tests/test_flows/test_flow.py | 23 +++++++++++++++++++++++ tests/test_runs/test_run_functions.py | 1 - 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/test_flows/test_flow.py b/tests/test_flows/test_flow.py index 50d152192..0c7f75fcb 100644 --- a/tests/test_flows/test_flow.py +++ b/tests/test_flows/test_flow.py @@ -555,3 +555,26 @@ def test_download_non_scikit_learn_flows(self): self.assertIsNone(subflow_2.parameters["batch-size"]) self.assertEqual(len(subflow_2.components), 0) self.assertIsNone(subflow_2.model) + + def test_delete_flow(self): + flow = openml.OpenMLFlow( + name="sklearn.dummy.DummyClassifier", + class_name="sklearn.dummy.DummyClassifier", + description="test description", + model=sklearn.dummy.DummyClassifier(), + components=collections.OrderedDict(), + parameters=collections.OrderedDict(), + parameters_meta_info=collections.OrderedDict(), + external_version=self.extension._format_external_version( + "sklearn", sklearn.__version__, + ), + tags=[], + language="English", + dependencies=None, + ) + + flow, _ = self._add_sentinel_to_flow_name(flow, None) + + flow.publish() + _run_id = flow.run_id + self.assertTrue(openml.flows.delete_flow(_flow_id)) diff --git a/tests/test_runs/test_run_functions.py b/tests/test_runs/test_run_functions.py index c60e42d7f..9e08f55a2 100644 --- a/tests/test_runs/test_run_functions.py +++ b/tests/test_runs/test_run_functions.py @@ -1763,6 +1763,5 @@ def test_delete_run(self): TestBase._mark_entity_for_removal("run", run.run_id) TestBase.logger.info("collected from test_run_functions: {}".format(run.run_id)) - _run_id = run.run_id self.assertTrue(delete_run(_run_id)) From c2cfeaa8ee3d8cc512bae157d838373264321118 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Thu, 1 Apr 2021 15:06:44 +0200 Subject: [PATCH 12/39] Correction of test_delete_flow unit test --- tests/test_flows/test_flow.py | 22 ---------------------- tests/test_flows/test_flow_functions.py | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/tests/test_flows/test_flow.py b/tests/test_flows/test_flow.py index 0c7f75fcb..a703f6a9b 100644 --- a/tests/test_flows/test_flow.py +++ b/tests/test_flows/test_flow.py @@ -556,25 +556,3 @@ def test_download_non_scikit_learn_flows(self): self.assertEqual(len(subflow_2.components), 0) self.assertIsNone(subflow_2.model) - def test_delete_flow(self): - flow = openml.OpenMLFlow( - name="sklearn.dummy.DummyClassifier", - class_name="sklearn.dummy.DummyClassifier", - description="test description", - model=sklearn.dummy.DummyClassifier(), - components=collections.OrderedDict(), - parameters=collections.OrderedDict(), - parameters_meta_info=collections.OrderedDict(), - external_version=self.extension._format_external_version( - "sklearn", sklearn.__version__, - ), - tags=[], - language="English", - dependencies=None, - ) - - flow, _ = self._add_sentinel_to_flow_name(flow, None) - - flow.publish() - _run_id = flow.run_id - self.assertTrue(openml.flows.delete_flow(_flow_id)) diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index fe058df23..efffa6d13 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -403,3 +403,24 @@ def test_get_flow_id(self): ) self.assertEqual(flow_ids_exact_version_True, flow_ids_exact_version_False) self.assertIn(flow.flow_id, flow_ids_exact_version_True) + + def test_delete_flow(self): + flow = openml.OpenMLFlow( + name="sklearn.dummy.DummyClassifier", + class_name="sklearn.dummy.DummyClassifier", + description="test description", + model=sklearn.dummy.DummyClassifier(), + components=OrderedDict(), + parameters=OrderedDict(), + parameters_meta_info=OrderedDict(), + external_version="1", + tags=[], + language="English", + dependencies=None, + ) + + flow, _ = self._add_sentinel_to_flow_name(flow, None) + + flow.publish() + _flow_id = flow.flow_id + self.assertTrue(openml.flows.delete_flow(_flow_id)) From e9ade63aa66dd014869f9f8ecdb3d96591f3a538 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Tue, 20 Apr 2021 15:51:04 +0200 Subject: [PATCH 13/39] Add test_delete_dataset() unit test for deleting database --- tests/test_datasets/test_dataset_functions.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index e6c4fe3ec..02cc8362d 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -1672,3 +1672,49 @@ def test_valid_attribute_validations(default_target_attribute, row_id_attribute, original_data_url=original_data_url, paper_url=paper_url, ) + + def test_delete_dataset(self): + data = [ + ["a", "sunny", 85.0, 85.0, "FALSE", "no"], + ["b", "sunny", 80.0, 90.0, "TRUE", "no"], + ["c", "overcast", 83.0, 86.0, "FALSE", "yes"], + ["d", "rainy", 70.0, 96.0, "FALSE", "yes"], + ["e", "rainy", 68.0, 80.0, "FALSE", "yes"], + ] + column_names = ["rnd_str", "outlook", "temperature", "humidity", "windy", "play"] + df = pd.DataFrame(data, columns=column_names) + # enforce the type of each column + df["outlook"] = df["outlook"].astype("category") + df["windy"] = df["windy"].astype("bool") + df["play"] = df["play"].astype("category") + # meta-information + name = "%s-pandas_testing_dataset" % self._get_sentinel() + description = "Synthetic dataset created from a Pandas DataFrame" + creator = "OpenML tester" + collection_date = "01-01-2018" + language = "English" + licence = "MIT" + citation = "None" + original_data_url = "http://openml.github.io/openml-python" + paper_url = "http://openml.github.io/openml-python" + dataset = openml.datasets.functions.create_dataset( + name=name, + description=description, + creator=creator, + contributor=None, + collection_date=collection_date, + language=language, + licence=licence, + default_target_attribute="play", + row_id_attribute=None, + ignore_attribute=None, + citation=citation, + attributes="auto", + data=df, + version_label="test", + original_data_url=original_data_url, + paper_url=paper_url, + ) + dataset.publish() + _dataset_id = dataset.id + self.assertTrue(openml.datasets.delete_dataset(_dataset_id)) From 993b12ac9bfa5993cc08da28fcefeb8eb9a38bec Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Tue, 20 Apr 2021 16:04:23 +0200 Subject: [PATCH 14/39] pre-commit changes --- openml/datasets/functions.py | 1 + openml/flows/__init__.py | 10 +++++----- openml/flows/functions.py | 2 +- openml/runs/functions.py | 1 + openml/tasks/functions.py | 2 +- tests/test_flows/test_flow.py | 1 - tests/test_runs/test_run_functions.py | 20 ++++++++++---------- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/openml/datasets/functions.py b/openml/datasets/functions.py index 99678e21f..976ca06bc 100644 --- a/openml/datasets/functions.py +++ b/openml/datasets/functions.py @@ -1272,6 +1272,7 @@ def _get_online_dataset_format(dataset_id): # build a dict from the xml and get the format from the dataset description return xmltodict.parse(dataset_xml)["oml:data_set_description"]["oml:format"].lower() + def delete_dataset(dataset_id: int) -> bool: """Deletes a dataset from the OpenML server. diff --git a/openml/flows/__init__.py b/openml/flows/__init__.py index dd1bf82ca..f8d35c3f5 100644 --- a/openml/flows/__init__.py +++ b/openml/flows/__init__.py @@ -3,11 +3,11 @@ from .flow import OpenMLFlow from .functions import ( - get_flow, - list_flows, - flow_exists, - get_flow_id, - assert_flows_equal, + get_flow, + list_flows, + flow_exists, + get_flow_id, + assert_flows_equal, delete_flow, ) diff --git a/openml/flows/functions.py b/openml/flows/functions.py index 00f759187..4cfb34bbd 100644 --- a/openml/flows/functions.py +++ b/openml/flows/functions.py @@ -548,6 +548,7 @@ def _create_flow_from_xml(flow_xml: str) -> OpenMLFlow: return OpenMLFlow._from_dict(xmltodict.parse(flow_xml)) + def delete_flow(flow_id: int) -> bool: """Deletes a flow from the OpenML server. @@ -562,4 +563,3 @@ def delete_flow(flow_id: int) -> bool: True if the deletion was successful. False otherwise """ return openml.utils._delete_entity("flow", flow_id) - \ No newline at end of file diff --git a/openml/runs/functions.py b/openml/runs/functions.py index 145e07c76..be3a338c8 100644 --- a/openml/runs/functions.py +++ b/openml/runs/functions.py @@ -1206,6 +1206,7 @@ def format_prediction( else: raise NotImplementedError(f"Formatting for {type(task)} is not supported.") + def delete_run(run_id: int) -> bool: """Deletes a run from the OpenML server. diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index 1363ad52b..2e6009adf 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -546,6 +546,7 @@ def create_task( **kwargs, ) + def delete_task(task_id: int) -> bool: """Deletes a task from the OpenML server. @@ -560,4 +561,3 @@ def delete_task(task_id: int) -> bool: True if the deletion was successful. False otherwise """ return openml.utils._delete_entity("task", task_id) - \ No newline at end of file diff --git a/tests/test_flows/test_flow.py b/tests/test_flows/test_flow.py index a703f6a9b..50d152192 100644 --- a/tests/test_flows/test_flow.py +++ b/tests/test_flows/test_flow.py @@ -555,4 +555,3 @@ def test_download_non_scikit_learn_flows(self): self.assertIsNone(subflow_2.parameters["batch-size"]) self.assertEqual(len(subflow_2.components), 0) self.assertIsNone(subflow_2.model) - diff --git a/tests/test_runs/test_run_functions.py b/tests/test_runs/test_run_functions.py index 9e08f55a2..c57987dd5 100644 --- a/tests/test_runs/test_run_functions.py +++ b/tests/test_runs/test_run_functions.py @@ -24,7 +24,12 @@ import openml.extensions.sklearn from openml.testing import TestBase, SimpleImputer, CustomImputer from openml.extensions.sklearn import cat, cont -from openml.runs.functions import _run_task_get_arffcontent, run_exists, format_prediction, delete_run +from openml.runs.functions import ( + _run_task_get_arffcontent, + run_exists, + format_prediction, + delete_run, +) from openml.runs.trace import OpenMLRunTrace from openml.tasks import TaskType from openml.testing import check_task_existence @@ -1749,19 +1754,14 @@ def test_joblib_backends(self, parallel_mock): def test_delete_run(self): rs = 1 clf = sklearn.pipeline.Pipeline( - steps=[ - ('imputer', SimpleImputer()), - ('estimator', DecisionTreeClassifier()), - ] - ) + steps=[("imputer", SimpleImputer()), ("estimator", DecisionTreeClassifier())] + ) task = openml.tasks.get_task(32) # diabetes; crossvalidation - run = openml.runs.run_model_on_task( - model=clf, task=task, seed=rs - ) + run = openml.runs.run_model_on_task(model=clf, task=task, seed=rs) run.publish() TestBase._mark_entity_for_removal("run", run.run_id) TestBase.logger.info("collected from test_run_functions: {}".format(run.run_id)) - + _run_id = run.run_id self.assertTrue(delete_run(_run_id)) From 2030127ce71b88afb9a39eaf08e5c6cc9e99a638 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Tue, 20 Apr 2021 23:38:13 +0200 Subject: [PATCH 15/39] adding delete functions for run, task, flow and database to the api.rst --- doc/api.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index 86bfd121e..288bf66fb 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -38,6 +38,7 @@ Dataset Functions attributes_arff_from_df check_datasets_active create_dataset + delete_dataset get_dataset get_datasets list_datasets @@ -103,6 +104,7 @@ Flow Functions :template: function.rst assert_flows_equal + delete_flow flow_exists get_flow list_flows @@ -133,6 +135,7 @@ Run Functions :toctree: generated/ :template: function.rst + delete_run get_run get_runs get_run_trace @@ -251,6 +254,7 @@ Task Functions :template: function.rst create_task + delete_task get_task get_tasks list_tasks From bb922a52d89eca415c8fffb0b53dec5f373b9481 Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Wed, 3 Mar 2021 08:06:51 +0100 Subject: [PATCH 16/39] Adding delete_task() to task/functions.py --- openml/tasks/functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index 2e6009adf..657f4b71f 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -546,7 +546,6 @@ def create_task( **kwargs, ) - def delete_task(task_id: int) -> bool: """Deletes a task from the OpenML server. From 0c18ab902733a5672f4a22d493357986719bbceb Mon Sep 17 00:00:00 2001 From: Mirkazemi Date: Tue, 20 Apr 2021 16:04:23 +0200 Subject: [PATCH 17/39] pre-commit changes --- openml/tasks/functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index 657f4b71f..2e6009adf 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -546,6 +546,7 @@ def create_task( **kwargs, ) + def delete_task(task_id: int) -> bool: """Deletes a task from the OpenML server. From ebcd522a9003cd46fa33b32352917b66e553cb5a Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Thu, 23 Feb 2023 15:27:06 +0100 Subject: [PATCH 18/39] [no ci] Update docstring to specify when the entity can be deleted --- openml/datasets/functions.py | 7 +++++-- openml/flows/functions.py | 7 +++++-- openml/runs/functions.py | 6 ++++-- openml/tasks/functions.py | 7 +++++-- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/openml/datasets/functions.py b/openml/datasets/functions.py index 976ca06bc..bd269c813 100644 --- a/openml/datasets/functions.py +++ b/openml/datasets/functions.py @@ -1274,7 +1274,10 @@ def _get_online_dataset_format(dataset_id): def delete_dataset(dataset_id: int) -> bool: - """Deletes a dataset from the OpenML server. + """Delete dataset with id `dataset_id` from the OpenML server. + + This can only be done if you are the owner of the dataset and + no tasks are attached to the dataset. Parameters ---------- @@ -1284,6 +1287,6 @@ def delete_dataset(dataset_id: int) -> bool: Returns ------- bool - True if the deletion was successful. False otherwise + True if the deletion was successful. False otherwise. """ return openml.utils._delete_entity("dataset", dataset_id) diff --git a/openml/flows/functions.py b/openml/flows/functions.py index 4cfb34bbd..fc66a3431 100644 --- a/openml/flows/functions.py +++ b/openml/flows/functions.py @@ -550,7 +550,10 @@ def _create_flow_from_xml(flow_xml: str) -> OpenMLFlow: def delete_flow(flow_id: int) -> bool: - """Deletes a flow from the OpenML server. + """Delete flow with id `flow_id` from the OpenML server. + + You can only delete flows which you uploaded and which + which are not linked to runs. Parameters ---------- @@ -560,6 +563,6 @@ def delete_flow(flow_id: int) -> bool: Returns ------- bool - True if the deletion was successful. False otherwise + True if the deletion was successful. False otherwise. """ return openml.utils._delete_entity("flow", flow_id) diff --git a/openml/runs/functions.py b/openml/runs/functions.py index be3a338c8..c55f8f073 100644 --- a/openml/runs/functions.py +++ b/openml/runs/functions.py @@ -1208,7 +1208,9 @@ def format_prediction( def delete_run(run_id: int) -> bool: - """Deletes a run from the OpenML server. + """Delete run with id `run_id` from the OpenML server. + + You can only delete runs which you uploaded. Parameters ---------- @@ -1218,6 +1220,6 @@ def delete_run(run_id: int) -> bool: Returns ------- bool - True if the deletion was successful. False otherwise + True if the deletion was successful. False otherwise. """ return openml.utils._delete_entity("run", run_id) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index 2e6009adf..964277760 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -548,7 +548,10 @@ def create_task( def delete_task(task_id: int) -> bool: - """Deletes a task from the OpenML server. + """Delete task with id `task_id` from the OpenML server. + + You can only delete tasks which you created and have + no runs associated with them. Parameters ---------- @@ -558,6 +561,6 @@ def delete_task(task_id: int) -> bool: Returns ------- bool - True if the deletion was successful. False otherwise + True if the deletion was successful. False otherwise. """ return openml.utils._delete_entity("task", task_id) From 7d0ce6614177e06490f16f3cd23f9cd2a8ab19cb Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 24 Feb 2023 09:58:15 +0100 Subject: [PATCH 19/39] Add responses from OpenML Server for task delete --- tests/files/misc/task_delete_has_runs.xml | 4 ++++ tests/files/misc/task_delete_not_owned.xml | 4 ++++ tests/files/misc/task_delete_successful.xml | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 tests/files/misc/task_delete_has_runs.xml create mode 100644 tests/files/misc/task_delete_not_owned.xml create mode 100644 tests/files/misc/task_delete_successful.xml diff --git a/tests/files/misc/task_delete_has_runs.xml b/tests/files/misc/task_delete_has_runs.xml new file mode 100644 index 000000000..87a92540d --- /dev/null +++ b/tests/files/misc/task_delete_has_runs.xml @@ -0,0 +1,4 @@ + + 454 + Task is executed in some runs. Delete these first + diff --git a/tests/files/misc/task_delete_not_owned.xml b/tests/files/misc/task_delete_not_owned.xml new file mode 100644 index 000000000..3d504772b --- /dev/null +++ b/tests/files/misc/task_delete_not_owned.xml @@ -0,0 +1,4 @@ + + 453 + Task is not owned by you + diff --git a/tests/files/misc/task_delete_successful.xml b/tests/files/misc/task_delete_successful.xml new file mode 100644 index 000000000..594b6e992 --- /dev/null +++ b/tests/files/misc/task_delete_successful.xml @@ -0,0 +1,3 @@ + + 361323 + From 1210f118431443e75e4517d7d25648b7731365b8 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 24 Feb 2023 10:07:40 +0100 Subject: [PATCH 20/39] Add OpenMLUnauthorizedError for fail on authenticated request --- openml/exceptions.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/openml/exceptions.py b/openml/exceptions.py index a5f132128..d627998b7 100644 --- a/openml/exceptions.py +++ b/openml/exceptions.py @@ -11,15 +11,14 @@ class OpenMLServerError(PyOpenMLError): """class for when something is really wrong on the server (result did not parse to dict), contains unparsed error.""" - def __init__(self, message: str): - super().__init__(message) + pass class OpenMLServerException(OpenMLServerError): """exception for when the result of the server was not 200 (e.g., listing call w/o results).""" - # Code needs to be optional to allow the exceptino to be picklable: + # Code needs to be optional to allow the exception to be picklable: # https://stackoverflow.com/questions/16244923/how-to-make-a-custom-exception-class-with-multiple-init-args-pickleable # noqa: E501 def __init__(self, message: str, code: int = None, url: str = None): self.message = message @@ -36,7 +35,7 @@ def __str__(self): class OpenMLServerNoResult(OpenMLServerException): - """exception for when the result of the server is empty.""" + """Exception for when the result of the server is empty.""" pass @@ -44,8 +43,7 @@ class OpenMLServerNoResult(OpenMLServerException): class OpenMLCacheException(PyOpenMLError): """Dataset / task etc not found in cache""" - def __init__(self, message: str): - super().__init__(message) + pass class OpenMLHashException(PyOpenMLError): @@ -57,8 +55,7 @@ class OpenMLHashException(PyOpenMLError): class OpenMLPrivateDatasetError(PyOpenMLError): """Exception thrown when the user has no rights to access the dataset.""" - def __init__(self, message: str): - super().__init__(message) + pass class OpenMLRunsExistError(PyOpenMLError): @@ -69,3 +66,9 @@ def __init__(self, run_ids: set, message: str): raise ValueError("Set of run ids must be non-empty.") self.run_ids = run_ids super().__init__(message) + + +class OpenMLNotAuthorizedError(OpenMLServerException): + """Indicates an authenticated user is not authorized to execute the requested action.""" + + pass From 09db1404a5609bd92645b97ca64a21985fe62d71 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 24 Feb 2023 10:10:58 +0100 Subject: [PATCH 21/39] Improve error messaging for expected errors --- openml/tasks/functions.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index 964277760..d994ddd4d 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -9,7 +9,7 @@ import pandas as pd import xmltodict -from ..exceptions import OpenMLCacheException +from ..exceptions import OpenMLCacheException, OpenMLServerException, OpenMLNotAuthorizedError from ..datasets import get_dataset from .task import ( OpenMLClassificationTask, @@ -563,4 +563,20 @@ def delete_task(task_id: int) -> bool: bool True if the deletion was successful. False otherwise. """ - return openml.utils._delete_entity("task", task_id) + try: + return openml.utils._delete_entity("task", task_id) + except OpenMLServerException as e: + # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php#L234-L239 # noqa: 501 + if e.code == 453: + raise OpenMLNotAuthorizedError( + message="The task can not be deleted because it was not uploaded by you.", + code=e.code, + url=e.url, + ) + if e.code == 454: + raise OpenMLNotAuthorizedError( + message="The task can not be deleted because it still has associated runs.", + code=e.code, + url=e.url, + ) + raise From 5e061d38cc9ce42ffabca48cea1f816d6e2c2432 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 24 Feb 2023 10:11:38 +0100 Subject: [PATCH 22/39] Add tests for task delete --- tests/test_tasks/test_task_functions.py | 70 ++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index be5b0c9bd..888d8a87e 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -3,10 +3,13 @@ import os from unittest import mock +import pytest +import requests + from openml.tasks import TaskType from openml.testing import TestBase from openml import OpenMLSplit, OpenMLTask -from openml.exceptions import OpenMLCacheException +from openml.exceptions import OpenMLCacheException, OpenMLNotAuthorizedError import openml import unittest import pandas as pd @@ -253,3 +256,68 @@ def test_deletion_of_cache_dir(self): self.assertTrue(os.path.exists(tid_cache_dir)) openml.utils._remove_cache_dir_for_id("tasks", tid_cache_dir) self.assertFalse(os.path.exists(tid_cache_dir)) + + @mock.patch.object(requests.Session, "delete") + def test_delete_task_not_owned(self, mock_get): + openml.config.start_using_configuration_for_example() + with open(self.static_cache_dir + "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/misc/task_delete_not_owned.xml", "r") as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 453 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The task can not be deleted because it was not uploaded by you.", + ): + openml.tasks.delete_task(1) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/1",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + @mock.patch.object(requests.Session, "delete") + def test_delete_task_with_run(self, mock_get): + openml.config.start_using_configuration_for_example() + with open(self.static_cache_dir + "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/misc/task_delete_has_runs.xml", "r") as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 454 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The task can not be deleted because it still has associated runs.", + ): + openml.tasks.delete_task(3496) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/3496",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + @mock.patch.object(requests.Session, "delete") + def test_delete_success(self, mock_get): + openml.config.start_using_configuration_for_example() + with open(self.static_cache_dir + "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/misc/task_delete_successful.xml", "r") as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 200 + response._content = response_body.encode() + mock_get.return_value = response + + openml.tasks.delete_task(361323) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/361323",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) From bba3cdf37cfca9c4978c74c683069b030c128fd4 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 3 Mar 2023 16:58:32 +0100 Subject: [PATCH 23/39] Improve documentation and test for forwarding server errors --- openml/_api_calls.py | 10 ++++++---- tests/files/misc/task_delete_not_exist.xml | 0 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 tests/files/misc/task_delete_not_exist.xml diff --git a/openml/_api_calls.py b/openml/_api_calls.py index f3c3306fc..7938b8c4a 100644 --- a/openml/_api_calls.py +++ b/openml/_api_calls.py @@ -339,10 +339,12 @@ def _send_request(request_method, url, data, files=None, md5_checksum=None): xml.parsers.expat.ExpatError, OpenMLHashException, ) as e: - if isinstance(e, OpenMLServerException): - if e.code not in [107]: - # 107: database connection error - raise + if isinstance(e, OpenMLServerException) and e.code != 107: + # Propagate all server errors to the calling functions, except + # for 107 which represents a database connection error. + # These are typically caused by high server load, + # which means trying again might resolve the issue. + raise elif isinstance(e, xml.parsers.expat.ExpatError): if request_method != "get" or retry_counter >= n_retries: raise OpenMLServerError( diff --git a/tests/files/misc/task_delete_not_exist.xml b/tests/files/misc/task_delete_not_exist.xml new file mode 100644 index 000000000..e69de29bb From 926f14682b448debbab058117c801e835fa65b4c Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 3 Mar 2023 16:59:36 +0100 Subject: [PATCH 24/39] Document unwrapped error codes and add explicit 455 error --- openml/tasks/functions.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index d994ddd4d..57758ad79 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -567,6 +567,8 @@ def delete_task(task_id: int) -> bool: return openml.utils._delete_entity("task", task_id) except OpenMLServerException as e: # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php#L234-L239 # noqa: 501 + # 451 isn't actually used but 103 is returned instead + # 452 is descriptive enough: 'Task does not exist - None' if e.code == 453: raise OpenMLNotAuthorizedError( message="The task can not be deleted because it was not uploaded by you.", @@ -579,4 +581,13 @@ def delete_task(task_id: int) -> bool: code=e.code, url=e.url, ) + if e.code == 455: + raise OpenMLServerException( + message=( + "The task can not be deleted for unknown reason, please open an issue at: " + "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/openml/openml-python/issues/new" + ), + code=e.code, + url=e.url, + ) raise From f4fc886b0dd250e66e0a933609eb8e0e0f181038 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 3 Mar 2023 17:00:20 +0100 Subject: [PATCH 25/39] Add test for attempting to delete a task that doesnt exist --- tests/files/misc/task_delete_not_exist.xml | 4 ++++ tests/test_tasks/test_task_functions.py | 28 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/tests/files/misc/task_delete_not_exist.xml b/tests/files/misc/task_delete_not_exist.xml index e69de29bb..8a262af29 100644 --- a/tests/files/misc/task_delete_not_exist.xml +++ b/tests/files/misc/task_delete_not_exist.xml @@ -0,0 +1,4 @@ + + 452 + Task does not exist + diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index 888d8a87e..f1ce49653 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -9,7 +9,7 @@ from openml.tasks import TaskType from openml.testing import TestBase from openml import OpenMLSplit, OpenMLTask -from openml.exceptions import OpenMLCacheException, OpenMLNotAuthorizedError +from openml.exceptions import OpenMLCacheException, OpenMLNotAuthorizedError, OpenMLServerException import openml import unittest import pandas as pd @@ -314,10 +314,34 @@ def test_delete_success(self, mock_get): response._content = response_body.encode() mock_get.return_value = response - openml.tasks.delete_task(361323) + success = openml.tasks.delete_task(361323) + assert success expected_call_args = [ ("https://test.openml.org/api/v1/xml/task/361323",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] assert expected_call_args == list(mock_get.call_args) + + @mock.patch.object(requests.Session, "delete") + def test_delete_unknown_task(self, mock_get): + openml.config.start_using_configuration_for_example() + with open(self.static_cache_dir + "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/misc/task_delete_not_exist.xml", "r") as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 412 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLServerException, + match="Task does not exist", + ): + openml.tasks.delete_task(9_999_999) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/9999999",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) From ebc5630586ea8598e5e6ec7daf19ce9b6c91baa9 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 3 Mar 2023 17:07:54 +0100 Subject: [PATCH 26/39] Move cached server responses to dedicated directory --- .../tasks}/task_delete_has_runs.xml | 0 .../tasks}/task_delete_not_exist.xml | 0 .../tasks}/task_delete_not_owned.xml | 0 .../tasks}/task_delete_successful.xml | 0 tests/test_tasks/test_task_functions.py | 16 ++++++++++++---- 5 files changed, 12 insertions(+), 4 deletions(-) rename tests/files/{misc => mock_responses/tasks}/task_delete_has_runs.xml (100%) rename tests/files/{misc => mock_responses/tasks}/task_delete_not_exist.xml (100%) rename tests/files/{misc => mock_responses/tasks}/task_delete_not_owned.xml (100%) rename tests/files/{misc => mock_responses/tasks}/task_delete_successful.xml (100%) diff --git a/tests/files/misc/task_delete_has_runs.xml b/tests/files/mock_responses/tasks/task_delete_has_runs.xml similarity index 100% rename from tests/files/misc/task_delete_has_runs.xml rename to tests/files/mock_responses/tasks/task_delete_has_runs.xml diff --git a/tests/files/misc/task_delete_not_exist.xml b/tests/files/mock_responses/tasks/task_delete_not_exist.xml similarity index 100% rename from tests/files/misc/task_delete_not_exist.xml rename to tests/files/mock_responses/tasks/task_delete_not_exist.xml diff --git a/tests/files/misc/task_delete_not_owned.xml b/tests/files/mock_responses/tasks/task_delete_not_owned.xml similarity index 100% rename from tests/files/misc/task_delete_not_owned.xml rename to tests/files/mock_responses/tasks/task_delete_not_owned.xml diff --git a/tests/files/misc/task_delete_successful.xml b/tests/files/mock_responses/tasks/task_delete_successful.xml similarity index 100% rename from tests/files/misc/task_delete_successful.xml rename to tests/files/mock_responses/tasks/task_delete_successful.xml diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index f1ce49653..02e7fdd09 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -260,7 +260,9 @@ def test_deletion_of_cache_dir(self): @mock.patch.object(requests.Session, "delete") def test_delete_task_not_owned(self, mock_get): openml.config.start_using_configuration_for_example() - with open(self.static_cache_dir + "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/misc/task_delete_not_owned.xml", "r") as xml_response: + with open( + self.static_cache_dir + "/mock_responses/tasks/task_delete_not_owned.xml", "r" + ) as xml_response: response_body = xml_response.read() response = requests.Response() @@ -283,7 +285,9 @@ def test_delete_task_not_owned(self, mock_get): @mock.patch.object(requests.Session, "delete") def test_delete_task_with_run(self, mock_get): openml.config.start_using_configuration_for_example() - with open(self.static_cache_dir + "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/misc/task_delete_has_runs.xml", "r") as xml_response: + with open( + self.static_cache_dir + "/mock_responses/tasks/task_delete_has_runs.xml", "r" + ) as xml_response: response_body = xml_response.read() response = requests.Response() @@ -306,7 +310,9 @@ def test_delete_task_with_run(self, mock_get): @mock.patch.object(requests.Session, "delete") def test_delete_success(self, mock_get): openml.config.start_using_configuration_for_example() - with open(self.static_cache_dir + "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/misc/task_delete_successful.xml", "r") as xml_response: + with open( + self.static_cache_dir + "/mock_responses/tasks/task_delete_successful.xml", "r" + ) as xml_response: response_body = xml_response.read() response = requests.Response() @@ -326,7 +332,9 @@ def test_delete_success(self, mock_get): @mock.patch.object(requests.Session, "delete") def test_delete_unknown_task(self, mock_get): openml.config.start_using_configuration_for_example() - with open(self.static_cache_dir + "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/misc/task_delete_not_exist.xml", "r") as xml_response: + with open( + self.static_cache_dir + "/mock_responses/tasks/task_delete_not_exist.xml", "r" + ) as xml_response: response_body = xml_response.read() response = requests.Response() From 7efb78c85602fe78b370160595badfcdfa6a0ea3 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 3 Mar 2023 17:26:18 +0100 Subject: [PATCH 27/39] Move exception wrapping down one level --- openml/tasks/functions.py | 31 ++----------------------------- openml/utils.py | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py index 57758ad79..964277760 100644 --- a/openml/tasks/functions.py +++ b/openml/tasks/functions.py @@ -9,7 +9,7 @@ import pandas as pd import xmltodict -from ..exceptions import OpenMLCacheException, OpenMLServerException, OpenMLNotAuthorizedError +from ..exceptions import OpenMLCacheException from ..datasets import get_dataset from .task import ( OpenMLClassificationTask, @@ -563,31 +563,4 @@ def delete_task(task_id: int) -> bool: bool True if the deletion was successful. False otherwise. """ - try: - return openml.utils._delete_entity("task", task_id) - except OpenMLServerException as e: - # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php#L234-L239 # noqa: 501 - # 451 isn't actually used but 103 is returned instead - # 452 is descriptive enough: 'Task does not exist - None' - if e.code == 453: - raise OpenMLNotAuthorizedError( - message="The task can not be deleted because it was not uploaded by you.", - code=e.code, - url=e.url, - ) - if e.code == 454: - raise OpenMLNotAuthorizedError( - message="The task can not be deleted because it still has associated runs.", - code=e.code, - url=e.url, - ) - if e.code == 455: - raise OpenMLServerException( - message=( - "The task can not be deleted for unknown reason, please open an issue at: " - "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/openml/openml-python/issues/new" - ), - code=e.code, - url=e.url, - ) - raise + return openml.utils._delete_entity("task", task_id) diff --git a/openml/utils.py b/openml/utils.py index 8ab238463..4e19dcde9 100644 --- a/openml/utils.py +++ b/openml/utils.py @@ -172,12 +172,36 @@ def _delete_entity(entity_type, entity_id): raise ValueError("Can't delete a %s" % entity_type) url_suffix = "%s/%d" % (entity_type, entity_id) - result_xml = openml._api_calls._perform_api_call(url_suffix, "delete") - result = xmltodict.parse(result_xml) - if "oml:%s_delete" % entity_type in result: - return True - else: - return False + try: + result_xml = openml._api_calls._perform_api_call(url_suffix, "delete") + result = xmltodict.parse(result_xml) + return f"oml:{entity_type}_delete" in result + except openml.exceptions.OpenMLServerException as e: + # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php#L234-L239 # noqa: 501 + # 451 isn't actually used but 103 is returned instead + # 452 is descriptive enough: 'Task does not exist - None' + if e.code == 453: + raise openml.exceptions.OpenMLNotAuthorizedError( + message="The task can not be deleted because it was not uploaded by you.", + code=e.code, + url=e.url, + ) + if e.code == 454: + raise openml.exceptions.OpenMLNotAuthorizedError( + message="The task can not be deleted because it still has associated runs.", + code=e.code, + url=e.url, + ) + if e.code == 455: + raise openml.exceptions.OpenMLServerException( + message=( + "The task can not be deleted for unknown reason, please open an issue at: " + "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/openml/openml-python/issues/new" + ), + code=e.code, + url=e.url, + ) + raise def _list_all(listing_call, output_format="dict", *args, **filters): From 7ab3d2635d8be5d9b3476b9077258b5499aea868 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Mon, 6 Mar 2023 16:02:05 +0100 Subject: [PATCH 28/39] Centralize error wrapping for delete calls --- openml/exceptions.py | 8 ++--- openml/utils.py | 46 ++++++++++++++----------- tests/test_tasks/test_task_functions.py | 2 +- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/openml/exceptions.py b/openml/exceptions.py index d627998b7..fe2138e76 100644 --- a/openml/exceptions.py +++ b/openml/exceptions.py @@ -27,11 +27,7 @@ def __init__(self, message: str, code: int = None, url: str = None): super().__init__(message) def __str__(self): - return "%s returned code %s: %s" % ( - self.url, - self.code, - self.message, - ) + return f"{self.url} returned code {self.code}: {self.message}" class OpenMLServerNoResult(OpenMLServerException): @@ -68,7 +64,7 @@ def __init__(self, run_ids: set, message: str): super().__init__(message) -class OpenMLNotAuthorizedError(OpenMLServerException): +class OpenMLNotAuthorizedError(OpenMLServerError): """Indicates an authenticated user is not authorized to execute the requested action.""" pass diff --git a/openml/utils.py b/openml/utils.py index 4e19dcde9..3c2fa876f 100644 --- a/openml/utils.py +++ b/openml/utils.py @@ -177,30 +177,36 @@ def _delete_entity(entity_type, entity_id): result = xmltodict.parse(result_xml) return f"oml:{entity_type}_delete" in result except openml.exceptions.OpenMLServerException as e: - # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php#L234-L239 # noqa: 501 - # 451 isn't actually used but 103 is returned instead - # 452 is descriptive enough: 'Task does not exist - None' - if e.code == 453: + # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php + # Most exceptions are descriptive enough to be raised as their standard + # OpenMLServerException, however there are two cases where we add information: + # - a generic "failed" message, we direct them to the right issue board + # - when the user successfully authenticates with the server, + # but user is not allowed to take the requested action, + # in which case we specify a OpenMLNotAuthorizedError. + by_other_user = [323, 353, 393, 453, 594] + has_dependent_entities = [324, 326, 327, 328, 354, 454, 464, 595] + unknown_reason = [325, 355, 394, 455, 593] + if e.code in by_other_user: raise openml.exceptions.OpenMLNotAuthorizedError( - message="The task can not be deleted because it was not uploaded by you.", - code=e.code, - url=e.url, - ) - if e.code == 454: + message=( + f"The {entity_type} can not be deleted because it was not uploaded by you." + ), + ) from e + if e.code in has_dependent_entities: raise openml.exceptions.OpenMLNotAuthorizedError( - message="The task can not be deleted because it still has associated runs.", - code=e.code, - url=e.url, - ) - if e.code == 455: - raise openml.exceptions.OpenMLServerException( message=( - "The task can not be deleted for unknown reason, please open an issue at: " - "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/openml/openml-python/issues/new" + f"The {entity_type} can not be deleted because " + f"it still has associated entities: {e.message}" + ) + ) from e + if e.code in unknown_reason: + raise openml.exceptions.OpenMLServerError( + message=( + f"The {entity_type} can not be deleted for unknown reason," + " please open an issue at: https://github.com/openml/openml/issues/new" ), - code=e.code, - url=e.url, - ) + ) from e raise diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index 02e7fdd09..e58ae2f9c 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -297,7 +297,7 @@ def test_delete_task_with_run(self, mock_get): with pytest.raises( OpenMLNotAuthorizedError, - match="The task can not be deleted because it still has associated runs.", + match="The task can not be deleted because it still has associated entities:", ): openml.tasks.delete_task(3496) From 610b0a3c141bde9cc1cf411eb2bac467d9d4a3a2 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Mon, 6 Mar 2023 16:20:55 +0100 Subject: [PATCH 29/39] Switch to Pytest style for new tests --- tests/conftest.py | 6 + tests/test_tasks/test_task_functions.py | 196 ++++++++++++------------ 2 files changed, 106 insertions(+), 96 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index cf3f33834..d0fccf271 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ import os import logging +import pathlib from typing import List import pytest @@ -186,3 +187,8 @@ def pytest_addoption(parser): @pytest.fixture(scope="class") def long_version(request): request.cls.long_version = request.config.getoption("--long") + + +@pytest.fixture +def test_files_directory() -> pathlib.Path: + return pathlib.Path(__file__).parent / "files" diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index e58ae2f9c..fe249fa43 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -257,99 +257,103 @@ def test_deletion_of_cache_dir(self): openml.utils._remove_cache_dir_for_id("tasks", tid_cache_dir) self.assertFalse(os.path.exists(tid_cache_dir)) - @mock.patch.object(requests.Session, "delete") - def test_delete_task_not_owned(self, mock_get): - openml.config.start_using_configuration_for_example() - with open( - self.static_cache_dir + "/mock_responses/tasks/task_delete_not_owned.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 453 - response._content = response_body.encode() - mock_get.return_value = response - - with pytest.raises( - OpenMLNotAuthorizedError, - match="The task can not be deleted because it was not uploaded by you.", - ): - openml.tasks.delete_task(1) - - expected_call_args = [ - ("https://test.openml.org/api/v1/xml/task/1",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, - ] - assert expected_call_args == list(mock_get.call_args) - - @mock.patch.object(requests.Session, "delete") - def test_delete_task_with_run(self, mock_get): - openml.config.start_using_configuration_for_example() - with open( - self.static_cache_dir + "/mock_responses/tasks/task_delete_has_runs.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 454 - response._content = response_body.encode() - mock_get.return_value = response - - with pytest.raises( - OpenMLNotAuthorizedError, - match="The task can not be deleted because it still has associated entities:", - ): - openml.tasks.delete_task(3496) - - expected_call_args = [ - ("https://test.openml.org/api/v1/xml/task/3496",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, - ] - assert expected_call_args == list(mock_get.call_args) - - @mock.patch.object(requests.Session, "delete") - def test_delete_success(self, mock_get): - openml.config.start_using_configuration_for_example() - with open( - self.static_cache_dir + "/mock_responses/tasks/task_delete_successful.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 200 - response._content = response_body.encode() - mock_get.return_value = response - - success = openml.tasks.delete_task(361323) - assert success - - expected_call_args = [ - ("https://test.openml.org/api/v1/xml/task/361323",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, - ] - assert expected_call_args == list(mock_get.call_args) - - @mock.patch.object(requests.Session, "delete") - def test_delete_unknown_task(self, mock_get): - openml.config.start_using_configuration_for_example() - with open( - self.static_cache_dir + "/mock_responses/tasks/task_delete_not_exist.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 412 - response._content = response_body.encode() - mock_get.return_value = response - - with pytest.raises( - OpenMLServerException, - match="Task does not exist", - ): - openml.tasks.delete_task(9_999_999) - - expected_call_args = [ - ("https://test.openml.org/api/v1/xml/task/9999999",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, - ] - assert expected_call_args == list(mock_get.call_args) + +@mock.patch.object(requests.Session, "delete") +def test_delete_task_not_owned(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 453 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The task can not be deleted because it was not uploaded by you.", + ): + openml.tasks.delete_task(1) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/1",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_task_with_run(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 454 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The task can not be deleted because it still has associated entities:", + ): + openml.tasks.delete_task(3496) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/3496",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_success(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "tasks" / "task_delete_successful.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 200 + response._content = response_body.encode() + mock_get.return_value = response + + success = openml.tasks.delete_task(361323) + assert success + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/361323",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_unknown_task(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "tasks" / "task_delete_not_exist.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 412 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLServerException, + match="Task does not exist", + ): + openml.tasks.delete_task(9_999_999) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/task/9999999",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) From 96869c4fed31c464dfb3b53dbd93036af098a988 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Mon, 6 Mar 2023 17:15:59 +0100 Subject: [PATCH 30/39] Add dataset delete tests --- openml/datasets/functions.py | 2 +- .../datasets/data_delete_has_tasks.xml | 4 + .../datasets/data_delete_not_exist.xml | 4 + .../datasets/data_delete_not_owned.xml | 4 + .../datasets/data_delete_successful.xml | 3 + tests/test_datasets/test_dataset_functions.py | 103 ++++++++++++++++++ 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/files/mock_responses/datasets/data_delete_has_tasks.xml create mode 100644 tests/files/mock_responses/datasets/data_delete_not_exist.xml create mode 100644 tests/files/mock_responses/datasets/data_delete_not_owned.xml create mode 100644 tests/files/mock_responses/datasets/data_delete_successful.xml diff --git a/openml/datasets/functions.py b/openml/datasets/functions.py index bd269c813..4307c8008 100644 --- a/openml/datasets/functions.py +++ b/openml/datasets/functions.py @@ -1289,4 +1289,4 @@ def delete_dataset(dataset_id: int) -> bool: bool True if the deletion was successful. False otherwise. """ - return openml.utils._delete_entity("dataset", dataset_id) + return openml.utils._delete_entity("data", dataset_id) diff --git a/tests/files/mock_responses/datasets/data_delete_has_tasks.xml b/tests/files/mock_responses/datasets/data_delete_has_tasks.xml new file mode 100644 index 000000000..fc866047c --- /dev/null +++ b/tests/files/mock_responses/datasets/data_delete_has_tasks.xml @@ -0,0 +1,4 @@ + + 354 + Dataset is in use by other content. Can not be deleted + diff --git a/tests/files/mock_responses/datasets/data_delete_not_exist.xml b/tests/files/mock_responses/datasets/data_delete_not_exist.xml new file mode 100644 index 000000000..b3b212fbe --- /dev/null +++ b/tests/files/mock_responses/datasets/data_delete_not_exist.xml @@ -0,0 +1,4 @@ + + 352 + Dataset does not exist + diff --git a/tests/files/mock_responses/datasets/data_delete_not_owned.xml b/tests/files/mock_responses/datasets/data_delete_not_owned.xml new file mode 100644 index 000000000..7d412d48e --- /dev/null +++ b/tests/files/mock_responses/datasets/data_delete_not_owned.xml @@ -0,0 +1,4 @@ + + 353 + Dataset is not owned by you + \ No newline at end of file diff --git a/tests/files/mock_responses/datasets/data_delete_successful.xml b/tests/files/mock_responses/datasets/data_delete_successful.xml new file mode 100644 index 000000000..9df47c1a2 --- /dev/null +++ b/tests/files/mock_responses/datasets/data_delete_successful.xml @@ -0,0 +1,3 @@ + + 40000 + diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index 02cc8362d..fc7925569 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -13,6 +13,7 @@ import pytest import numpy as np import pandas as pd +import requests import scipy.sparse from oslo_concurrency import lockutils @@ -23,6 +24,7 @@ OpenMLHashException, OpenMLPrivateDatasetError, OpenMLServerException, + OpenMLNotAuthorizedError, ) from openml.testing import TestBase from openml.utils import _tag_entity, _create_cache_directory_for_id @@ -1718,3 +1720,104 @@ def test_delete_dataset(self): dataset.publish() _dataset_id = dataset.id self.assertTrue(openml.datasets.delete_dataset(_dataset_id)) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_dataset_not_owned(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 353 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The data can not be deleted because it was not uploaded by you.", + ): + openml.datasets.delete_dataset(40_000) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/data/40000",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_dataset_with_run(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 354 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The data can not be deleted because it still has associated entities:", + ): + openml.datasets.delete_dataset(40_000) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/data/40000",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_dataset_success(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "datasets" / "data_delete_successful.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 200 + response._content = response_body.encode() + mock_get.return_value = response + + success = openml.datasets.delete_dataset(40000) + assert success + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/data/40000",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_unknown_dataset(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 352 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLServerException, + match="Dataset does not exist", + ): + openml.datasets.delete_dataset(9_999_999) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/data/9999999",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) From 0d387b31b2be199211203e8dfbdec17c176400b3 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Mon, 6 Mar 2023 17:40:12 +0100 Subject: [PATCH 31/39] Add flow delete tests --- .../flows/flow_delete_has_runs.xml | 5 + .../flows/flow_delete_is_subflow.xml | 5 + .../flows/flow_delete_not_exist.xml | 4 + .../flows/flow_delete_not_owned.xml | 4 + .../flows/flow_delete_successful.xml | 3 + tests/test_flows/test_flow_functions.py | 131 ++++++++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 tests/files/mock_responses/flows/flow_delete_has_runs.xml create mode 100644 tests/files/mock_responses/flows/flow_delete_is_subflow.xml create mode 100644 tests/files/mock_responses/flows/flow_delete_not_exist.xml create mode 100644 tests/files/mock_responses/flows/flow_delete_not_owned.xml create mode 100644 tests/files/mock_responses/flows/flow_delete_successful.xml diff --git a/tests/files/mock_responses/flows/flow_delete_has_runs.xml b/tests/files/mock_responses/flows/flow_delete_has_runs.xml new file mode 100644 index 000000000..5c8530e75 --- /dev/null +++ b/tests/files/mock_responses/flows/flow_delete_has_runs.xml @@ -0,0 +1,5 @@ + + 324 + flow is in use by other content (runs). Can not be deleted + {10716, 10707} () + diff --git a/tests/files/mock_responses/flows/flow_delete_is_subflow.xml b/tests/files/mock_responses/flows/flow_delete_is_subflow.xml new file mode 100644 index 000000000..ddc314ae4 --- /dev/null +++ b/tests/files/mock_responses/flows/flow_delete_is_subflow.xml @@ -0,0 +1,5 @@ + + 328 + flow is in use by other content (it is a subflow). Can not be deleted + {37661} + diff --git a/tests/files/mock_responses/flows/flow_delete_not_exist.xml b/tests/files/mock_responses/flows/flow_delete_not_exist.xml new file mode 100644 index 000000000..4df49149f --- /dev/null +++ b/tests/files/mock_responses/flows/flow_delete_not_exist.xml @@ -0,0 +1,4 @@ + + 322 + flow does not exist + diff --git a/tests/files/mock_responses/flows/flow_delete_not_owned.xml b/tests/files/mock_responses/flows/flow_delete_not_owned.xml new file mode 100644 index 000000000..3aa9a9ef2 --- /dev/null +++ b/tests/files/mock_responses/flows/flow_delete_not_owned.xml @@ -0,0 +1,4 @@ + + 323 + flow is not owned by you + diff --git a/tests/files/mock_responses/flows/flow_delete_successful.xml b/tests/files/mock_responses/flows/flow_delete_successful.xml new file mode 100644 index 000000000..7638e942d --- /dev/null +++ b/tests/files/mock_responses/flows/flow_delete_successful.xml @@ -0,0 +1,3 @@ + + 33364 + diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index efffa6d13..79750c889 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -4,15 +4,19 @@ import copy import functools import unittest +from unittest import mock from unittest.mock import patch from distutils.version import LooseVersion + +import requests import sklearn from sklearn import ensemble import pandas as pd import pytest import openml +from openml.exceptions import OpenMLNotAuthorizedError, OpenMLServerException from openml.testing import TestBase import openml.extensions.sklearn @@ -424,3 +428,130 @@ def test_delete_flow(self): flow.publish() _flow_id = flow.flow_id self.assertTrue(openml.flows.delete_flow(_flow_id)) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_flow_not_owned(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 323 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The flow can not be deleted because it was not uploaded by you.", + ): + openml.flows.delete_flow(40_000) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/flow/40000",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_flow_with_run(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 324 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The flow can not be deleted because it still has associated entities:", + ): + openml.flows.delete_flow(40_000) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/flow/40000",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_subflow(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 328 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The flow can not be deleted because it still has associated entities:", + ): + openml.flows.delete_flow(40_000) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/flow/40000",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_flow_success(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 200 + response._content = response_body.encode() + mock_get.return_value = response + + success = openml.flows.delete_flow(33364) + assert success + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/flow/33364",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_unknown_flow(mock_get, test_files_directory): + openml.config.start_using_configuration_for_example() + with open( + test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml", "r" + ) as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = 322 + response._content = response_body.encode() + mock_get.return_value = response + + with pytest.raises( + OpenMLServerException, + match="flow does not exist", + ): + openml.flows.delete_flow(9_999_999) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/flow/9999999",), + {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + ] + assert expected_call_args == list(mock_get.call_args) From 230035b00e93cfccf7f86f89355f6a126682456d Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Mon, 6 Mar 2023 17:46:02 +0100 Subject: [PATCH 32/39] Extract response creation --- openml/testing.py | 22 ++++++- tests/test_datasets/test_dataset_functions.py | 54 ++++++------------ tests/test_flows/test_flow_functions.py | 57 ++++--------------- tests/test_tasks/test_task_functions.py | 46 +++------------ 4 files changed, 58 insertions(+), 121 deletions(-) diff --git a/openml/testing.py b/openml/testing.py index 56445a253..4e2f0c006 100644 --- a/openml/testing.py +++ b/openml/testing.py @@ -3,12 +3,14 @@ import hashlib import inspect import os +import pathlib import shutil import sys import time from typing import Dict, Union, cast import unittest import pandas as pd +import requests import openml from openml.tasks import TaskType @@ -306,4 +308,22 @@ class CustomImputer(SimpleImputer): pass -__all__ = ["TestBase", "SimpleImputer", "CustomImputer", "check_task_existence"] +def create_request_response( + *, status_code: int, content_filepath: pathlib.Path +) -> requests.Response: + with open(content_filepath, "r") as xml_response: + response_body = xml_response.read() + + response = requests.Response() + response.status_code = status_code + response._content = response_body.encode() + return response + + +__all__ = [ + "TestBase", + "SimpleImputer", + "CustomImputer", + "check_task_existence", + "create_request_response", +] diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index fc7925569..790dfa4a7 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -26,7 +26,7 @@ OpenMLServerException, OpenMLNotAuthorizedError, ) -from openml.testing import TestBase +from openml.testing import TestBase, create_request_response from openml.utils import _tag_entity, _create_cache_directory_for_id from openml.datasets.functions import ( create_dataset, @@ -1725,15 +1725,10 @@ def test_delete_dataset(self): @mock.patch.object(requests.Session, "delete") def test_delete_dataset_not_owned(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 353 - response._content = response_body.encode() - mock_get.return_value = response + content_file = ( + test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml" + ) + mock_get.return_value = create_request_response(status_code=353, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -1751,15 +1746,10 @@ def test_delete_dataset_not_owned(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_dataset_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 354 - response._content = response_body.encode() - mock_get.return_value = response + content_file = ( + test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml" + ) + mock_get.return_value = create_request_response(status_code=354, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -1777,15 +1767,10 @@ def test_delete_dataset_with_run(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_dataset_success(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "datasets" / "data_delete_successful.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 200 - response._content = response_body.encode() - mock_get.return_value = response + content_file = ( + test_files_directory / "mock_responses" / "datasets" / "data_delete_successful.xml" + ) + mock_get.return_value = create_request_response(status_code=200, content_filepath=content_file) success = openml.datasets.delete_dataset(40000) assert success @@ -1800,15 +1785,10 @@ def test_delete_dataset_success(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_unknown_dataset(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 352 - response._content = response_body.encode() - mock_get.return_value = response + content_file = ( + test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml" + ) + mock_get.return_value = create_request_response(status_code=352, content_filepath=content_file) with pytest.raises( OpenMLServerException, diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index 79750c889..b7a53739c 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -17,7 +17,7 @@ import openml from openml.exceptions import OpenMLNotAuthorizedError, OpenMLServerException -from openml.testing import TestBase +from openml.testing import TestBase, create_request_response import openml.extensions.sklearn @@ -433,15 +433,8 @@ def test_delete_flow(self): @mock.patch.object(requests.Session, "delete") def test_delete_flow_not_owned(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 323 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml" + mock_get.return_value = create_request_response(status_code=323, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -459,15 +452,8 @@ def test_delete_flow_not_owned(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_flow_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 324 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml" + mock_get.return_value = create_request_response(status_code=324, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -485,15 +471,8 @@ def test_delete_flow_with_run(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_subflow(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 328 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml" + mock_get.return_value = create_request_response(status_code=328, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -511,15 +490,8 @@ def test_delete_subflow(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_flow_success(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 200 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml" + mock_get.return_value = create_request_response(status_code=200, content_filepath=content_file) success = openml.flows.delete_flow(33364) assert success @@ -534,15 +506,8 @@ def test_delete_flow_success(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_unknown_flow(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 322 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml" + mock_get.return_value = create_request_response(status_code=322, content_filepath=content_file) with pytest.raises( OpenMLServerException, diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index fe249fa43..1fbb7152c 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -7,7 +7,7 @@ import requests from openml.tasks import TaskType -from openml.testing import TestBase +from openml.testing import TestBase, create_request_response from openml import OpenMLSplit, OpenMLTask from openml.exceptions import OpenMLCacheException, OpenMLNotAuthorizedError, OpenMLServerException import openml @@ -261,15 +261,8 @@ def test_deletion_of_cache_dir(self): @mock.patch.object(requests.Session, "delete") def test_delete_task_not_owned(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 453 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml" + mock_get.return_value = create_request_response(status_code=453, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -287,15 +280,8 @@ def test_delete_task_not_owned(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_task_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 454 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml" + mock_get.return_value = create_request_response(status_code=454, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -313,15 +299,8 @@ def test_delete_task_with_run(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_success(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "tasks" / "task_delete_successful.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 200 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_successful.xml" + mock_get.return_value = create_request_response(status_code=200, content_filepath=content_file) success = openml.tasks.delete_task(361323) assert success @@ -336,15 +315,8 @@ def test_delete_success(mock_get, test_files_directory): @mock.patch.object(requests.Session, "delete") def test_delete_unknown_task(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() - with open( - test_files_directory / "mock_responses" / "tasks" / "task_delete_not_exist.xml", "r" - ) as xml_response: - response_body = xml_response.read() - - response = requests.Response() - response.status_code = 412 - response._content = response_body.encode() - mock_get.return_value = response + content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_exist.xml" + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLServerException, From cff3c46e92b9b03a49425316c699a2815f634bfe Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Tue, 14 Mar 2023 11:20:39 +0100 Subject: [PATCH 33/39] HTTP status code should always be 412, openml code is in xml --- tests/test_datasets/test_dataset_functions.py | 6 +++--- tests/test_flows/test_flow_functions.py | 6 +++--- tests/test_tasks/test_task_functions.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index 790dfa4a7..08f20065c 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -1728,7 +1728,7 @@ def test_delete_dataset_not_owned(mock_get, test_files_directory): content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml" ) - mock_get.return_value = create_request_response(status_code=353, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -1749,7 +1749,7 @@ def test_delete_dataset_with_run(mock_get, test_files_directory): content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml" ) - mock_get.return_value = create_request_response(status_code=354, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -1788,7 +1788,7 @@ def test_delete_unknown_dataset(mock_get, test_files_directory): content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml" ) - mock_get.return_value = create_request_response(status_code=352, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLServerException, diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index b7a53739c..2e96877b3 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -453,7 +453,7 @@ def test_delete_flow_not_owned(mock_get, test_files_directory): def test_delete_flow_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml" - mock_get.return_value = create_request_response(status_code=324, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -472,7 +472,7 @@ def test_delete_flow_with_run(mock_get, test_files_directory): def test_delete_subflow(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml" - mock_get.return_value = create_request_response(status_code=328, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -507,7 +507,7 @@ def test_delete_flow_success(mock_get, test_files_directory): def test_delete_unknown_flow(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml" - mock_get.return_value = create_request_response(status_code=322, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLServerException, diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index 1fbb7152c..771a0a90a 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -262,7 +262,7 @@ def test_deletion_of_cache_dir(self): def test_delete_task_not_owned(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml" - mock_get.return_value = create_request_response(status_code=453, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -281,7 +281,7 @@ def test_delete_task_not_owned(mock_get, test_files_directory): def test_delete_task_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml" - mock_get.return_value = create_request_response(status_code=454, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, From 382d1eb5abcffc38e283b796f7ee02a5fad77771 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Tue, 14 Mar 2023 11:20:39 +0100 Subject: [PATCH 34/39] HTTP status code should always be 412, openml code is in xml --- tests/test_datasets/test_dataset_functions.py | 6 +++--- tests/test_flows/test_flow_functions.py | 8 ++++---- tests/test_tasks/test_task_functions.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index 790dfa4a7..08f20065c 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -1728,7 +1728,7 @@ def test_delete_dataset_not_owned(mock_get, test_files_directory): content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml" ) - mock_get.return_value = create_request_response(status_code=353, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -1749,7 +1749,7 @@ def test_delete_dataset_with_run(mock_get, test_files_directory): content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml" ) - mock_get.return_value = create_request_response(status_code=354, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -1788,7 +1788,7 @@ def test_delete_unknown_dataset(mock_get, test_files_directory): content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml" ) - mock_get.return_value = create_request_response(status_code=352, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLServerException, diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index b7a53739c..b14001d08 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -434,7 +434,7 @@ def test_delete_flow(self): def test_delete_flow_not_owned(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml" - mock_get.return_value = create_request_response(status_code=323, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -453,7 +453,7 @@ def test_delete_flow_not_owned(mock_get, test_files_directory): def test_delete_flow_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml" - mock_get.return_value = create_request_response(status_code=324, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -472,7 +472,7 @@ def test_delete_flow_with_run(mock_get, test_files_directory): def test_delete_subflow(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml" - mock_get.return_value = create_request_response(status_code=328, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -507,7 +507,7 @@ def test_delete_flow_success(mock_get, test_files_directory): def test_delete_unknown_flow(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml" - mock_get.return_value = create_request_response(status_code=322, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLServerException, diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index 1fbb7152c..771a0a90a 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -262,7 +262,7 @@ def test_deletion_of_cache_dir(self): def test_delete_task_not_owned(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml" - mock_get.return_value = create_request_response(status_code=453, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, @@ -281,7 +281,7 @@ def test_delete_task_not_owned(mock_get, test_files_directory): def test_delete_task_with_run(mock_get, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml" - mock_get.return_value = create_request_response(status_code=454, content_filepath=content_file) + mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) with pytest.raises( OpenMLNotAuthorizedError, From b0071276a4f2d5e35b69f766ddae1da206dff0dc Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 17 Mar 2023 11:54:12 +0100 Subject: [PATCH 35/39] Rename mock_get to mock_delete --- tests/test_datasets/test_dataset_functions.py | 32 +++++++++------ tests/test_flows/test_flow_functions.py | 40 ++++++++++++------- tests/test_tasks/test_task_functions.py | 32 +++++++++------ 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index 08f20065c..b49e34d59 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -1723,12 +1723,14 @@ def test_delete_dataset(self): @mock.patch.object(requests.Session, "delete") -def test_delete_dataset_not_owned(mock_get, test_files_directory): +def test_delete_dataset_not_owned(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml" ) - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -1740,16 +1742,18 @@ def test_delete_dataset_not_owned(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/data/40000",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_dataset_with_run(mock_get, test_files_directory): +def test_delete_dataset_with_run(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml" ) - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -1761,16 +1765,18 @@ def test_delete_dataset_with_run(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/data/40000",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_dataset_success(mock_get, test_files_directory): +def test_delete_dataset_success(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_successful.xml" ) - mock_get.return_value = create_request_response(status_code=200, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=200, content_filepath=content_file + ) success = openml.datasets.delete_dataset(40000) assert success @@ -1779,16 +1785,18 @@ def test_delete_dataset_success(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/data/40000",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_unknown_dataset(mock_get, test_files_directory): +def test_delete_unknown_dataset(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml" ) - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLServerException, @@ -1800,4 +1808,4 @@ def test_delete_unknown_dataset(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/data/9999999",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index b14001d08..e49c9618a 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -431,10 +431,12 @@ def test_delete_flow(self): @mock.patch.object(requests.Session, "delete") -def test_delete_flow_not_owned(mock_get, test_files_directory): +def test_delete_flow_not_owned(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -446,14 +448,16 @@ def test_delete_flow_not_owned(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/flow/40000",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_flow_with_run(mock_get, test_files_directory): +def test_delete_flow_with_run(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -465,14 +469,16 @@ def test_delete_flow_with_run(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/flow/40000",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_subflow(mock_get, test_files_directory): +def test_delete_subflow(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -484,14 +490,16 @@ def test_delete_subflow(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/flow/40000",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_flow_success(mock_get, test_files_directory): +def test_delete_flow_success(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml" - mock_get.return_value = create_request_response(status_code=200, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=200, content_filepath=content_file + ) success = openml.flows.delete_flow(33364) assert success @@ -500,14 +508,16 @@ def test_delete_flow_success(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/flow/33364",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_unknown_flow(mock_get, test_files_directory): +def test_delete_unknown_flow(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLServerException, @@ -519,4 +529,4 @@ def test_delete_unknown_flow(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/flow/9999999",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index 771a0a90a..50b9b7ca0 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -259,10 +259,12 @@ def test_deletion_of_cache_dir(self): @mock.patch.object(requests.Session, "delete") -def test_delete_task_not_owned(mock_get, test_files_directory): +def test_delete_task_not_owned(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -274,14 +276,16 @@ def test_delete_task_not_owned(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/task/1",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_task_with_run(mock_get, test_files_directory): +def test_delete_task_with_run(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLNotAuthorizedError, @@ -293,14 +297,16 @@ def test_delete_task_with_run(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/task/3496",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_success(mock_get, test_files_directory): +def test_delete_success(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_successful.xml" - mock_get.return_value = create_request_response(status_code=200, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=200, content_filepath=content_file + ) success = openml.tasks.delete_task(361323) assert success @@ -309,14 +315,16 @@ def test_delete_success(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/task/361323",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_unknown_task(mock_get, test_files_directory): +def test_delete_unknown_task(mock_delete, test_files_directory): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_exist.xml" - mock_get.return_value = create_request_response(status_code=412, content_filepath=content_file) + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) with pytest.raises( OpenMLServerException, @@ -328,4 +336,4 @@ def test_delete_unknown_task(mock_get, test_files_directory): ("https://test.openml.org/api/v1/xml/task/9999999",), {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, ] - assert expected_call_args == list(mock_get.call_args) + assert expected_call_args == list(mock_delete.call_args) From fef5cbe6b7addf33a631bd65ff623882fd7fd03d Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 17 Mar 2023 12:01:40 +0100 Subject: [PATCH 36/39] Add test server api key as fixture --- tests/conftest.py | 5 +++++ tests/test_datasets/test_dataset_functions.py | 16 +++++++-------- tests/test_flows/test_flow_functions.py | 20 +++++++++---------- tests/test_tasks/test_task_functions.py | 16 +++++++-------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index d0fccf271..8df1b9d83 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -192,3 +192,8 @@ def long_version(request): @pytest.fixture def test_files_directory() -> pathlib.Path: return pathlib.Path(__file__).parent / "files" + + +@pytest.fixture() +def test_api_key() -> str: + return "c0c42819af31e706efe1f4b88c23c6c1" diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py index b49e34d59..45a64ab8a 100644 --- a/tests/test_datasets/test_dataset_functions.py +++ b/tests/test_datasets/test_dataset_functions.py @@ -1723,7 +1723,7 @@ def test_delete_dataset(self): @mock.patch.object(requests.Session, "delete") -def test_delete_dataset_not_owned(mock_delete, test_files_directory): +def test_delete_dataset_not_owned(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml" @@ -1740,13 +1740,13 @@ def test_delete_dataset_not_owned(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/data/40000",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_dataset_with_run(mock_delete, test_files_directory): +def test_delete_dataset_with_run(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml" @@ -1763,13 +1763,13 @@ def test_delete_dataset_with_run(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/data/40000",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_dataset_success(mock_delete, test_files_directory): +def test_delete_dataset_success(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_successful.xml" @@ -1783,13 +1783,13 @@ def test_delete_dataset_success(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/data/40000",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_unknown_dataset(mock_delete, test_files_directory): +def test_delete_unknown_dataset(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = ( test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml" @@ -1806,6 +1806,6 @@ def test_delete_unknown_dataset(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/data/9999999",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py index e49c9618a..4239bad93 100644 --- a/tests/test_flows/test_flow_functions.py +++ b/tests/test_flows/test_flow_functions.py @@ -431,7 +431,7 @@ def test_delete_flow(self): @mock.patch.object(requests.Session, "delete") -def test_delete_flow_not_owned(mock_delete, test_files_directory): +def test_delete_flow_not_owned(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml" mock_delete.return_value = create_request_response( @@ -446,13 +446,13 @@ def test_delete_flow_not_owned(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/flow/40000",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_flow_with_run(mock_delete, test_files_directory): +def test_delete_flow_with_run(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml" mock_delete.return_value = create_request_response( @@ -467,13 +467,13 @@ def test_delete_flow_with_run(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/flow/40000",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_subflow(mock_delete, test_files_directory): +def test_delete_subflow(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml" mock_delete.return_value = create_request_response( @@ -488,13 +488,13 @@ def test_delete_subflow(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/flow/40000",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_flow_success(mock_delete, test_files_directory): +def test_delete_flow_success(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml" mock_delete.return_value = create_request_response( @@ -506,13 +506,13 @@ def test_delete_flow_success(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/flow/33364",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_unknown_flow(mock_delete, test_files_directory): +def test_delete_unknown_flow(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml" mock_delete.return_value = create_request_response( @@ -527,6 +527,6 @@ def test_delete_unknown_flow(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/flow/9999999",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py index 50b9b7ca0..dde3561f4 100644 --- a/tests/test_tasks/test_task_functions.py +++ b/tests/test_tasks/test_task_functions.py @@ -259,7 +259,7 @@ def test_deletion_of_cache_dir(self): @mock.patch.object(requests.Session, "delete") -def test_delete_task_not_owned(mock_delete, test_files_directory): +def test_delete_task_not_owned(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml" mock_delete.return_value = create_request_response( @@ -274,13 +274,13 @@ def test_delete_task_not_owned(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/task/1",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_task_with_run(mock_delete, test_files_directory): +def test_delete_task_with_run(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml" mock_delete.return_value = create_request_response( @@ -295,13 +295,13 @@ def test_delete_task_with_run(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/task/3496",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_success(mock_delete, test_files_directory): +def test_delete_success(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_successful.xml" mock_delete.return_value = create_request_response( @@ -313,13 +313,13 @@ def test_delete_success(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/task/361323",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) @mock.patch.object(requests.Session, "delete") -def test_delete_unknown_task(mock_delete, test_files_directory): +def test_delete_unknown_task(mock_delete, test_files_directory, test_api_key): openml.config.start_using_configuration_for_example() content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_exist.xml" mock_delete.return_value = create_request_response( @@ -334,6 +334,6 @@ def test_delete_unknown_task(mock_delete, test_files_directory): expected_call_args = [ ("https://test.openml.org/api/v1/xml/task/9999999",), - {"params": {"api_key": "c0c42819af31e706efe1f4b88c23c6c1"}}, + {"params": {"api_key": test_api_key}}, ] assert expected_call_args == list(mock_delete.call_args) From d38721695a3952db796a0593a6dc85827ea97c95 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 17 Mar 2023 12:39:55 +0100 Subject: [PATCH 37/39] Add run delete tests --- .../runs/run_delete_not_exist.xml | 4 + .../runs/run_delete_not_owned.xml | 4 + .../runs/run_delete_successful.xml | 3 + tests/test_runs/test_run_functions.py | 78 +++++++++++++++++-- 4 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 tests/files/mock_responses/runs/run_delete_not_exist.xml create mode 100644 tests/files/mock_responses/runs/run_delete_not_owned.xml create mode 100644 tests/files/mock_responses/runs/run_delete_successful.xml diff --git a/tests/files/mock_responses/runs/run_delete_not_exist.xml b/tests/files/mock_responses/runs/run_delete_not_exist.xml new file mode 100644 index 000000000..855c223fa --- /dev/null +++ b/tests/files/mock_responses/runs/run_delete_not_exist.xml @@ -0,0 +1,4 @@ + + 392 + Run does not exist + diff --git a/tests/files/mock_responses/runs/run_delete_not_owned.xml b/tests/files/mock_responses/runs/run_delete_not_owned.xml new file mode 100644 index 000000000..551252e22 --- /dev/null +++ b/tests/files/mock_responses/runs/run_delete_not_owned.xml @@ -0,0 +1,4 @@ + + 393 + Run is not owned by you + diff --git a/tests/files/mock_responses/runs/run_delete_successful.xml b/tests/files/mock_responses/runs/run_delete_successful.xml new file mode 100644 index 000000000..fe4233afa --- /dev/null +++ b/tests/files/mock_responses/runs/run_delete_successful.xml @@ -0,0 +1,3 @@ + + 10591880 + diff --git a/tests/test_runs/test_run_functions.py b/tests/test_runs/test_run_functions.py index c57987dd5..c41f30e05 100644 --- a/tests/test_runs/test_run_functions.py +++ b/tests/test_runs/test_run_functions.py @@ -1,5 +1,4 @@ # License: BSD 3-Clause - import arff from distutils.version import LooseVersion import os @@ -7,10 +6,12 @@ import time import sys import ast -import unittest.mock +from unittest import mock import numpy as np import joblib +import pytest +import requests from joblib import parallel_backend import openml @@ -22,7 +23,7 @@ import pandas as pd import openml.extensions.sklearn -from openml.testing import TestBase, SimpleImputer, CustomImputer +from openml.testing import TestBase, SimpleImputer, CustomImputer, create_request_response from openml.extensions.sklearn import cat, cont from openml.runs.functions import ( _run_task_get_arffcontent, @@ -33,7 +34,10 @@ from openml.runs.trace import OpenMLRunTrace from openml.tasks import TaskType from openml.testing import check_task_existence -from openml.exceptions import OpenMLServerException +from openml.exceptions import ( + OpenMLServerException, + OpenMLNotAuthorizedError, +) from sklearn.naive_bayes import GaussianNB from sklearn.model_selection._search import BaseSearchCV @@ -699,7 +703,7 @@ def get_ct_cf(nominal_indices, numeric_indices): LooseVersion(sklearn.__version__) < "0.20", reason="columntransformer introduction in 0.20.0", ) - @unittest.mock.patch("warnings.warn") + @mock.patch("warnings.warn") def test_run_and_upload_knn_pipeline(self, warnings_mock): cat_imp = make_pipeline( @@ -1642,7 +1646,7 @@ def test_format_prediction_task_regression(self): LooseVersion(sklearn.__version__) < "0.21", reason="couldn't perform local tests successfully w/o bloating RAM", ) - @unittest.mock.patch("openml.extensions.sklearn.SklearnExtension._prevent_optimize_n_jobs") + @mock.patch("openml.extensions.sklearn.SklearnExtension._prevent_optimize_n_jobs") def test__run_task_get_arffcontent_2(self, parallel_mock): """Tests if a run executed in parallel is collated correctly.""" task = openml.tasks.get_task(7) # Supervised Classification on kr-vs-kp @@ -1695,7 +1699,7 @@ def test__run_task_get_arffcontent_2(self, parallel_mock): LooseVersion(sklearn.__version__) < "0.21", reason="couldn't perform local tests successfully w/o bloating RAM", ) - @unittest.mock.patch("openml.extensions.sklearn.SklearnExtension._prevent_optimize_n_jobs") + @mock.patch("openml.extensions.sklearn.SklearnExtension._prevent_optimize_n_jobs") def test_joblib_backends(self, parallel_mock): """Tests evaluation of a run using various joblib backends and n_jobs.""" task = openml.tasks.get_task(7) # Supervised Classification on kr-vs-kp @@ -1765,3 +1769,63 @@ def test_delete_run(self): _run_id = run.run_id self.assertTrue(delete_run(_run_id)) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_run_not_owned(mock_delete, test_files_directory, test_api_key): + openml.config.start_using_configuration_for_example() + content_file = test_files_directory / "mock_responses" / "runs" / "run_delete_not_owned.xml" + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) + + with pytest.raises( + OpenMLNotAuthorizedError, + match="The run can not be deleted because it was not uploaded by you.", + ): + openml.runs.delete_run(40_000) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/run/40000",), + {"params": {"api_key": test_api_key}}, + ] + assert expected_call_args == list(mock_delete.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_run_success(mock_delete, test_files_directory, test_api_key): + openml.config.start_using_configuration_for_example() + content_file = test_files_directory / "mock_responses" / "runs" / "run_delete_successful.xml" + mock_delete.return_value = create_request_response( + status_code=200, content_filepath=content_file + ) + + success = openml.runs.delete_run(10591880) + assert success + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/run/10591880",), + {"params": {"api_key": test_api_key}}, + ] + assert expected_call_args == list(mock_delete.call_args) + + +@mock.patch.object(requests.Session, "delete") +def test_delete_unknown_run(mock_delete, test_files_directory, test_api_key): + openml.config.start_using_configuration_for_example() + content_file = test_files_directory / "mock_responses" / "runs" / "run_delete_not_exist.xml" + mock_delete.return_value = create_request_response( + status_code=412, content_filepath=content_file + ) + + with pytest.raises( + OpenMLServerException, + match="Run does not exist", + ): + openml.runs.delete_run(9_999_999) + + expected_call_args = [ + ("https://test.openml.org/api/v1/xml/run/9999999",), + {"params": {"api_key": test_api_key}}, + ] + assert expected_call_args == list(mock_delete.call_args) From 54cfcda82e1198f2ff1e3d281383965e39c800ac Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Mon, 20 Mar 2023 17:04:24 +0100 Subject: [PATCH 38/39] Remove one of duplicate pytest import statement --- tests/test_runs/test_run_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_runs/test_run_functions.py b/tests/test_runs/test_run_functions.py index 8f7781c31..91dd4ce5e 100644 --- a/tests/test_runs/test_run_functions.py +++ b/tests/test_runs/test_run_functions.py @@ -10,7 +10,6 @@ import numpy as np import joblib -import pytest import requests from joblib import parallel_backend From d725c1a9b264d4513645de70e09e00a302833ba9 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Tue, 21 Mar 2023 09:17:44 +0100 Subject: [PATCH 39/39] [no ci] Deleting tasks, runs, datasets, and flows --- doc/progress.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/progress.rst b/doc/progress.rst index 48dc2a1a3..d981c09c0 100644 --- a/doc/progress.rst +++ b/doc/progress.rst @@ -10,10 +10,10 @@ Changelog ~~~~~~ * Add new contributions here. - * ADD#1144: Add locally computed results to the ``OpenMLRun`` object's representation. + * ADD#1028: Add functions to delete runs, flows, datasets, and tasks (e.g., ``openml.datasets.delete_dataset``). + * ADD#1144: Add locally computed results to the ``OpenMLRun`` object's representation if the run was created locally and not downloaded from the server. * FIX #1197 #559 #1131: Fix the order of ground truth and predictions in the ``OpenMLRun`` object and in ``format_prediction``. * FIX #1198: Support numpy 1.24 and higher. - * ADD#1144: Add locally computed results to the ``OpenMLRun`` object's representation if the run was created locally and not downloaded from the server. 0.13.0 ~~~~~~