From a6171b8174ba327b20e684b100c15b8c9dd50ab9 Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Tue, 7 Dec 2021 22:34:20 +0800 Subject: [PATCH 1/8] init Signed-off-by: Weichen Xu --- mlflow/models/model.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mlflow/models/model.py b/mlflow/models/model.py index bb55aa4c1cbfa..300527981b9bb 100644 --- a/mlflow/models/model.py +++ b/mlflow/models/model.py @@ -4,6 +4,7 @@ import yaml import os +import uuid from typing import Any, Dict, Optional @@ -41,6 +42,7 @@ def __init__( flavors=None, signature=None, # ModelSignature saved_input_example_info: Dict[str, Any] = None, + model_uuid=None, **kwargs, ): # store model id instead of run_id and path to avoid confusion when model gets exported @@ -52,6 +54,10 @@ def __init__( self.flavors = flavors if flavors is not None else {} self.signature = signature self.saved_input_example_info = saved_input_example_info + if self.model_uuid is None: + self.model_uuid = uuid.uuid4() + else: + self.model_uuid = model_uuid self.__dict__.update(kwargs) def __eq__(self, other): @@ -98,6 +104,8 @@ def to_dict(self): res["signature"] = self.signature.to_dict() if self.saved_input_example_info is not None: res["saved_input_example_info"] = self.saved_input_example_info + + res["model_uuid"] = self.model_uuid return res def to_yaml(self, stream=None): From e8ba5e89cbf1b5aeebdc1e73998c0355b5e7cb8f Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Tue, 7 Dec 2021 23:10:11 +0800 Subject: [PATCH 2/8] fix Signed-off-by: Weichen Xu --- mlflow/models/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mlflow/models/model.py b/mlflow/models/model.py index 300527981b9bb..b0a7c819927c7 100644 --- a/mlflow/models/model.py +++ b/mlflow/models/model.py @@ -54,8 +54,8 @@ def __init__( self.flavors = flavors if flavors is not None else {} self.signature = signature self.saved_input_example_info = saved_input_example_info - if self.model_uuid is None: - self.model_uuid = uuid.uuid4() + if model_uuid is None: + self.model_uuid = str(uuid.uuid4()) else: self.model_uuid = model_uuid self.__dict__.update(kwargs) From 5c0b03d319b4fb4b995d24a09e2e57985a3f2231 Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Wed, 8 Dec 2021 22:15:30 +0800 Subject: [PATCH 3/8] update Signed-off-by: Weichen Xu --- mlflow/models/model.py | 7 +------ ...test_model_export_with_loader_module_and_data_path.py | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mlflow/models/model.py b/mlflow/models/model.py index b0a7c819927c7..1ada7241bd406 100644 --- a/mlflow/models/model.py +++ b/mlflow/models/model.py @@ -54,10 +54,7 @@ def __init__( self.flavors = flavors if flavors is not None else {} self.signature = signature self.saved_input_example_info = saved_input_example_info - if model_uuid is None: - self.model_uuid = str(uuid.uuid4()) - else: - self.model_uuid = model_uuid + self.model_uuid = uuid.uuid4().hex if model_uuid is None else model_uuid self.__dict__.update(kwargs) def __eq__(self, other): @@ -104,8 +101,6 @@ def to_dict(self): res["signature"] = self.signature.to_dict() if self.saved_input_example_info is not None: res["saved_input_example_info"] = self.saved_input_example_info - - res["model_uuid"] = self.model_uuid return res def to_yaml(self, stream=None): diff --git a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py index 8b4f395096bc8..78c74fdc6a89c 100644 --- a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py +++ b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py @@ -556,6 +556,15 @@ def test_column_schema_enforcement_no_col_names(): assert pyfunc_model.predict(d).equals(pd.DataFrame(d)) +def test_model_uuid(): + m = Model() + assert m.model_uuid and len(m.model_uuid) == 32 + m_dict = m.to_dict() + assert m_dict['model_uuid'] == m.model_uuid + m2 = Model.from_dict(m_dict) + assert m2.model_uuid == m.model_uuid + + def test_tensor_schema_enforcement_no_col_names(): m = Model() input_schema = Schema([TensorSpec(np.dtype(np.float32), (-1, 3))]) From f4846fcb3b3675a8bb7c778165e12ceb60d5d6a5 Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Thu, 9 Dec 2021 10:15:43 +0800 Subject: [PATCH 4/8] check uuid valid Signed-off-by: Weichen Xu --- ...t_model_export_with_loader_module_and_data_path.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py index 78c74fdc6a89c..82b39bee1159d 100644 --- a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py +++ b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py @@ -556,9 +556,18 @@ def test_column_schema_enforcement_no_col_names(): assert pyfunc_model.predict(d).equals(pd.DataFrame(d)) +def _is_valid_uuid(val): + import uuid + try: + uuid.UUID(str(val)) + return True + except ValueError: + return False + + def test_model_uuid(): m = Model() - assert m.model_uuid and len(m.model_uuid) == 32 + assert m.model_uuid and _is_valid_uuid(m.model_uuid) m_dict = m.to_dict() assert m_dict['model_uuid'] == m.model_uuid m2 = Model.from_dict(m_dict) From 1ea970c569ae32d838384119204fea24ab88d2af Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Thu, 9 Dec 2021 10:22:16 +0800 Subject: [PATCH 5/8] update Signed-off-by: Weichen Xu --- .../test_model_export_with_loader_module_and_data_path.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py index 82b39bee1159d..7501884276a77 100644 --- a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py +++ b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py @@ -567,7 +567,8 @@ def _is_valid_uuid(val): def test_model_uuid(): m = Model() - assert m.model_uuid and _is_valid_uuid(m.model_uuid) + assert m.model_uuid is not None + assert _is_valid_uuid(m.model_uuid) m_dict = m.to_dict() assert m_dict['model_uuid'] == m.model_uuid m2 = Model.from_dict(m_dict) From 76c9efb8381346875948da28d7d5c8cde4703dce Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Thu, 9 Dec 2021 10:29:47 +0800 Subject: [PATCH 6/8] fix tests Signed-off-by: Weichen Xu --- .../test_model_export_with_loader_module_and_data_path.py | 3 ++- tests/tracking/test_rest_tracking.py | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py index 7501884276a77..6e9baca1e20df 100644 --- a/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py +++ b/tests/pyfunc/test_model_export_with_loader_module_and_data_path.py @@ -558,6 +558,7 @@ def test_column_schema_enforcement_no_col_names(): def _is_valid_uuid(val): import uuid + try: uuid.UUID(str(val)) return True @@ -570,7 +571,7 @@ def test_model_uuid(): assert m.model_uuid is not None assert _is_valid_uuid(m.model_uuid) m_dict = m.to_dict() - assert m_dict['model_uuid'] == m.model_uuid + assert m_dict["model_uuid"] == m.model_uuid m2 = Model.from_dict(m_dict) assert m2.model_uuid == m.model_uuid diff --git a/tests/tracking/test_rest_tracking.py b/tests/tracking/test_rest_tracking.py index 263e13229c1b9..1057e9a57479a 100644 --- a/tests/tracking/test_rest_tracking.py +++ b/tests/tracking/test_rest_tracking.py @@ -386,7 +386,12 @@ def test_log_model(mlflow_client, backend_store_uri): tag = run.data.tags["mlflow.log-model.history"] models = json.loads(tag) model.utc_time_created = models[i]["utc_time_created"] - assert models[i] == model.to_dict() + + history_model_meta = models[i].copy() + history_model_meta.pop("model_uuid") + model_meta = model.to_dict().copy() + model_meta.pop(("model_uuid")) + assert history_model_meta == model_meta assert len(models) == i + 1 for j in range(0, i + 1): assert models[j]["artifact_path"] == model_paths[j] From 8cdf1e9ada56736a18723cac70747d63a56067ee Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Thu, 9 Dec 2021 22:07:48 +0800 Subject: [PATCH 7/8] fix test Signed-off-by: Weichen Xu --- tests/models/test_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/models/test_model.py b/tests/models/test_model.py index df77f020be64f..3f058c765b87e 100644 --- a/tests/models/test_model.py +++ b/tests/models/test_model.py @@ -44,6 +44,7 @@ def test_model_save_load(): saved_input_example_info={"x": 1, "y": 2}, ) n.utc_time_created = m.utc_time_created + n.model_uuid = m.model_uuid assert m == n n.signature = None assert m != n From 02418628ccc5a4a8bab79bdc4adb2d6d942c5593 Mon Sep 17 00:00:00 2001 From: Weichen Xu Date: Sun, 12 Dec 2021 17:53:46 +0800 Subject: [PATCH 8/8] update test Signed-off-by: Weichen Xu --- tests/tracking/test_rest_tracking.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/tracking/test_rest_tracking.py b/tests/tracking/test_rest_tracking.py index 1057e9a57479a..b3b8adafb21d3 100644 --- a/tests/tracking/test_rest_tracking.py +++ b/tests/tracking/test_rest_tracking.py @@ -388,10 +388,11 @@ def test_log_model(mlflow_client, backend_store_uri): model.utc_time_created = models[i]["utc_time_created"] history_model_meta = models[i].copy() - history_model_meta.pop("model_uuid") + original_model_uuid = history_model_meta.pop("model_uuid") model_meta = model.to_dict().copy() - model_meta.pop(("model_uuid")) + new_model_uuid = model_meta.pop(("model_uuid")) assert history_model_meta == model_meta + assert original_model_uuid != new_model_uuid assert len(models) == i + 1 for j in range(0, i + 1): assert models[j]["artifact_path"] == model_paths[j]