Files
mlflow--mlflow/tests/johnsnowlabs/test_johnsnowlabs_model_export.py
2026-07-13 13:22:34 +08:00

748 lines
28 KiB
Python

import json
import os
from pathlib import Path
from unittest import mock
import pandas as pd
import pyspark
import pytest
from johnsnowlabs import nlp
from packaging.version import Version
import mlflow
from mlflow import pyfunc
from mlflow.environment_variables import MLFLOW_DFS_TMP
from mlflow.models import Model, infer_signature
from mlflow.models.utils import _read_example
from mlflow.pyfunc import spark_udf
from mlflow.utils.environment import _mlflow_conda_env
from mlflow.utils.file_utils import TempDir
from tests.helper_functions import (
assert_register_model_called_with_local_model_path,
)
MODEL_CACHE_FOLDER = None
nlu_model = "en.classify.bert_sequence.covid_sentiment"
def setup_env():
# Install licensed libraries on the fly into the current python environment
# and make them available in the executing python process
if "JSL_ACCESS_KEY" in os.environ:
# via access_token
from johnsnowlabs.py_models.jsl_secrets import JslSecrets
# Download License & Install Licensed Libraries
nlp.install(access_token=os.environ["JSL_ACCESS_KEY"])
# Write json secret to env
secrets = JslSecrets.from_jsl_home()
os.environ[mlflow.johnsnowlabs._JOHNSNOWLABS_ENV_JSON_LICENSE_KEY] = json.dumps({
"SECRET": secrets.HC_SECRET,
"AWS_ACCESS_KEY_ID": secrets.AWS_ACCESS_KEY_ID,
"AWS_SECRET_ACCESS_KEY": secrets.AWS_SECRET_ACCESS_KEY,
"SPARK_NLP_LICENSE": secrets.HC_LICENSE,
})
# mlflow.johnsnowlabs._JOHNSNOWLABS_JSON_VARS needs to be present now either from CI or from
# JSL_ACCESS_KEY
mlflow.johnsnowlabs._set_env_vars()
nlp.install()
@pytest.fixture(scope="module")
def load_and_init_model(model=nlu_model):
setup_env()
mlflow.johnsnowlabs._validate_env_vars()
nlp.start(model_cache_folder=MODEL_CACHE_FOLDER)
return nlp.load(model, verbose=False)
def fix_dataframe_with_respect_for_nlu_issues(df1, df2):
# When this issue is resolved, we can remove the usage of this function
# https://github.com/JohnSnowLabs/nlu/issues/new
# TODO there may be some changes in confidence and changes in column names after storing/loading
# a model these issues in NLU which are not related to MLflow and to be fixed.
# For now we are applying a hotfix here on the dataframes to make sure that the tests run the
# way they should
df1 = df1.drop(columns=[c for c in df1.columns if c not in df2.columns or "confidence" in c])
df2 = df2.drop(columns=[c for c in df2.columns if c not in df1.columns or "confidence" in c])
def lower_strings(df):
for c in df.columns:
try:
df[c] = df[c].str.lower()
except Exception:
pass
return df
df1 = lower_strings(df1)
df2 = lower_strings(df2)
# TODO fix: column names may change before/after save and Confidences change
df1.columns = [f"c_{i}" for i in range(len(df1.columns))]
df2.columns = [f"c_{i}" for i in range(len(df2.columns))]
return df1, df2
def validate_model(original_model, new_model):
df1 = original_model.predict("Hello World")
df2 = new_model.predict("Hello World")
if isinstance(df2, str):
df2 = (
pd
.DataFrame(json.loads(df2))
.drop(columns=["index"])
.reset_index()
.drop(columns=["index"])
)
else:
df2 = df2.reset_index().drop(columns=["index"])
df1 = df1.reset_index().drop(columns=["index"])
df1, df2 = fix_dataframe_with_respect_for_nlu_issues(df1, df2)
pd.testing.assert_frame_equal(df1, df2)
@pytest.fixture
def jsl_model(load_and_init_model):
return load_and_init_model
@pytest.fixture
def model_path(tmp_path):
return str(tmp_path / "model")
@pytest.fixture
def spark_custom_env(tmp_path):
conda_env = str(tmp_path / "conda_env.yml")
additional_pip_deps = ["pyspark", "pytest"]
if Version(pyspark.__version__) <= Version("3.3.2"):
# Versions of PySpark <= 3.3.2 are incompatible with pandas >= 2
additional_pip_deps.append("pandas<2")
_mlflow_conda_env(conda_env, additional_pip_deps=additional_pip_deps)
return conda_env
def score_model_as_udf(model_uri, result_type="string"):
spark = mlflow.johnsnowlabs._get_or_create_sparksession()
pandas_df = pd.DataFrame({"text": ["Hello World"]})
spark_df = spark.createDataFrame(pandas_df).coalesce(1)
pyfunc_udf = spark_udf(
spark=spark,
model_uri=model_uri,
env_manager="virtualenv",
result_type=result_type,
)
new_df = spark_df.withColumn("prediction", pyfunc_udf(*pandas_df.columns))
return [x["prediction"] for x in new_df.collect()]
def test_model_export(jsl_model, model_path):
mlflow.johnsnowlabs.save_model(jsl_model, path=model_path)
# 1. score and compare reloaded sparkml model
reloaded_model = mlflow.johnsnowlabs.load_model(model_uri=model_path)
validate_model(jsl_model, reloaded_model)
# 2. score and compare reloaded pyfunc
validate_model(jsl_model, pyfunc.load_model(model_path))
# 3. score and compare reloaded pyfunc Spark udf
preds3 = score_model_as_udf(model_uri=model_path)
df1 = (
pd
.DataFrame(json.loads(preds3[0]))
.drop(columns=["index"])
.reset_index()
.drop(columns=["index"])
)
df2 = jsl_model.predict("Hello world")
df1, df2 = fix_dataframe_with_respect_for_nlu_issues(df1, df2)
pd.testing.assert_frame_equal(df1, df2)
assert os.path.exists(MLFLOW_DFS_TMP.get())
#
# def test_model_deployment(jsl_model, model_path,spark_custom_env):
# # TODO test WIP
# # but using CLI mlflow models build-docker + docker run and mlflow models serve works fine
# import mlflow.pyfunc.scoring_server as pyfunc_scoring_server
#
# mlflow.johnsnowlabs.save_model(
# jsl_model,
# path=model_path,
# conda_env=spark_custom_env,
# )
# # pyfunc_build_image(
# # model_uri=model_path,
# # )
# # build_docker(
# # model_uri=model_path,
# # name="mlflow-pyfunc",
# # )
# scoring_response = score_model_in_sagemaker_docker_container(
# model_uri=model_path,
# data="Hello World",
# content_type=pyfunc_scoring_server.CONTENT_TYPE_JSON,
# flavor=mlflow.pyfunc.FLAVOR_NAME,
# )
# # TODO ASSERT EQ
#
def test_model_export_with_signature_and_examples(jsl_model):
example_ = jsl_model.predict("Hello World")
signature_ = infer_signature(example_)
for signature in (None, signature_):
for example in (None, example_):
with TempDir() as tmp:
path = tmp.path("model")
mlflow.johnsnowlabs.save_model(
jsl_model, path=path, signature=signature, input_example=example
)
mlflow_model = Model.load(path)
assert signature == mlflow_model.signature
if example is None:
assert mlflow_model.saved_input_example_info is None
else:
df1, df2 = fix_dataframe_with_respect_for_nlu_issues(
_read_example(mlflow_model, path).reset_index().drop(columns="index"),
example.reset_index().drop(columns="index"),
)
pd.testing.assert_frame_equal(df1, df2)
def test_log_model_with_signature_and_examples(jsl_model):
example_ = jsl_model.predict("Hello World")
signature_ = infer_signature(example_)
artifact_path = "model"
for signature in (None, signature_):
for example in (None, example_):
with mlflow.start_run():
mlflow.johnsnowlabs.log_model(
jsl_model,
name=artifact_path,
signature=signature,
input_example=example,
)
artifact_uri = mlflow.get_artifact_uri()
model_path = Path(artifact_uri) / artifact_path
mlflow_model = Model.load(model_path)
assert signature == mlflow_model.signature
if example is None:
assert mlflow_model.saved_input_example_info is None
else:
df1, df2 = fix_dataframe_with_respect_for_nlu_issues(
_read_example(mlflow_model, model_path).reset_index().drop(columns="index"),
example.reset_index().drop(columns="index"),
)
pd.testing.assert_frame_equal(df1, df2)
@pytest.mark.parametrize("should_start_run", [False, True])
@pytest.mark.parametrize("use_dfs_tmpdir", [False, True])
def test_johnsnowlabs_model_log(tmp_path, jsl_model, should_start_run, use_dfs_tmpdir):
old_tracking_uri = mlflow.get_tracking_uri()
dfs_tmpdir = None if use_dfs_tmpdir else tmp_path.joinpath("test")
try:
tracking_dir = tmp_path.joinpath("mlruns")
mlflow.set_tracking_uri(f"file://{tracking_dir}")
if should_start_run:
mlflow.start_run()
artifact_path = "model"
mlflow.johnsnowlabs.log_model(
jsl_model,
name=artifact_path,
dfs_tmpdir=dfs_tmpdir,
)
model_uri = f"runs:/{mlflow.active_run().info.run_id}/{artifact_path}"
reloaded_model = mlflow.johnsnowlabs.load_model(model_uri=model_uri, dfs_tmpdir=dfs_tmpdir)
validate_model(jsl_model, reloaded_model)
finally:
mlflow.end_run()
mlflow.set_tracking_uri(old_tracking_uri)
def test_log_model_calls_register_model(tmp_path, jsl_model):
artifact_path = "model"
dfs_tmp_dir = tmp_path.joinpath("test")
register_model_patch = mock.patch("mlflow.tracking._model_registry.fluent._register_model")
with mlflow.start_run(), register_model_patch:
mlflow.johnsnowlabs.log_model(
jsl_model,
name=artifact_path,
dfs_tmpdir=dfs_tmp_dir,
registered_model_name="AdsModel1",
)
model_uri = f"runs:/{mlflow.active_run().info.run_id}/{artifact_path}"
assert_register_model_called_with_local_model_path(
register_model_mock=mlflow.tracking._model_registry.fluent._register_model,
model_uri=model_uri,
registered_model_name="AdsModel1",
)
# def test_sagemaker_docker_model_scoring_with_default_conda_env(spark_model_iris, model_path):
# # TODO
# mlflow.johnsnowlabs.save_model(spark_model_iris.model, path=model_path)
#
# scoring_response = score_model_in_sagemaker_docker_container(
# model_uri=model_path,
# data=spark_model_iris.pandas_df,
# content_type=pyfunc_scoring_server.CONTENT_TYPE_JSON,
# flavor=mlflow.pyfunc.FLAVOR_NAME,
# )
# deployed_model_preds = np.array(json.loads(scoring_response.content)["predictions"])
#
# np.testing.assert_array_almost_equal(
# deployed_model_preds, spark_model_iris.predictions, decimal=4
# )
# def test_log_model_no_registered_model_name(tmpdir, jsl_model):
# artifact_path = "model"
# dfs_tmp_dir = Path(str(tmpdir)) / "test"
# register_model_patch = mock.patch("mlflow.tracking._model_registry.fluent._register_model")
# with mlflow.start_run(), register_model_patch:
# mlflow.johnsnowlabs.log_model(
# artifact_path=artifact_path,
# spark_model=jsl_model,
# dfs_tmpdir=dfs_tmp_dir,
# )
# mlflow.tracking._model_registry.fluent._register_model.assert_not_called()
# def test_johnsnowlabs_model_load_from_remote_uri_succeeds(jsl_model, model_path, mock_s3_bucket):
# mlflow.johnsnowlabs.save_model(spark_model=jsl_model, path=model_path)
#
# artifact_root = f"s3://{mock_s3_bucket}"
# artifact_path = "model"
# artifact_repo = S3ArtifactRepository(artifact_root)
# artifact_repo.log_artifacts(model_path, artifact_path=artifact_path)
#
# model_uri = artifact_root + "/" + artifact_path
# reloaded_model = mlflow.johnsnowlabs.load_model(model_uri=model_uri)
# validate_model(jsl_model, reloaded_model)
# def test_johnsnowlabs_model_save_persists_specified_conda_env_in_mlflow_model_directory(
# jsl_model, model_path, spark_custom_env
# ):
# mlflow.johnsnowlabs.save_model(
# spark_model=jsl_model, path=model_path, conda_env=spark_custom_env
# )
#
# pyfunc_conf = _get_flavor_configuration(model_path=model_path, flavor_name=pyfunc.FLAVOR_NAME)
# saved_conda_env_path = Path(model_path) / pyfunc_conf[pyfunc.ENV]["conda"]
# assert os.path.exists(saved_conda_env_path)
# assert saved_conda_env_path != spark_custom_env
#
# with open(spark_custom_env) as f:
# spark_custom_env_parsed = yaml.safe_load(f)
# with open(saved_conda_env_path) as f:
# saved_conda_env_parsed = yaml.safe_load(f)
# assert saved_conda_env_parsed == spark_custom_env_parsed
# def test_johnsnowlabs_model_save_persists_requirements_in_mlflow_model_directory(
# jsl_model, model_path, spark_custom_env
# ):
# mlflow.johnsnowlabs.save_model(
# spark_model=jsl_model, path=model_path, conda_env=spark_custom_env
# )
#
# saved_pip_req_path = Path(model_path) / "requirements.txt"
# _compare_conda_env_requirements(spark_custom_env, saved_pip_req_path)
# def test_log_model_with_pip_requirements(jsl_model, tmpdir):
# expected_mlflow_version = _mlflow_major_version_string()
# # Path to a requirements file
# req_file = tmpdir.join("requirements.txt")
# req_file.write("a")
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(jsl_model, "model", pip_requirements=req_file.strpath)
# _assert_pip_requirements(
# mlflow.get_artifact_uri("model"), [expected_mlflow_version, "a"], strict=True
# )
#
# # List of requirements
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(
# jsl_model, "model", pip_requirements=[f"-r {req_file.strpath}", "b"]
# )
# _assert_pip_requirements(
# mlflow.get_artifact_uri("model"), [expected_mlflow_version, "a", "b"], strict=True
# )
#
# # Constraints file
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(
# jsl_model, "model", pip_requirements=[f"-c {req_file.strpath}", "b"]
# )
# _assert_pip_requirements(
# mlflow.get_artifact_uri("model"),
# [expected_mlflow_version, "b", "-c constraints.txt"],
# ["a"],
# strict=True,
# )
#
#
# def test_log_model_with_extra_pip_requirements(jsl_model, tmpdir):
# expected_mlflow_version = _mlflow_major_version_string()
# default_reqs = mlflow.johnsnowlabs.get_default_pip_requirements()
#
# # Path to a requirements file
# req_file = tmpdir.join("requirements.txt")
# req_file.write("a")
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(jsl_model, "model", extra_pip_requirements=req_file.strpath)
# _assert_pip_requirements(
# mlflow.get_artifact_uri("model"), [expected_mlflow_version, *default_reqs, "a"]
# )
#
# # List of requirements
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(
# jsl_model, "model", extra_pip_requirements=[f"-r {req_file.strpath}", "b"]
# )
# _assert_pip_requirements(
# mlflow.get_artifact_uri("model"), [expected_mlflow_version, *default_reqs, "a", "b"]
# )
#
# # Constraints file
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(
# jsl_model, "model", extra_pip_requirements=[f"-c {req_file.strpath}", "b"]
# )
# _assert_pip_requirements(
# mlflow.get_artifact_uri("model"),
# [expected_mlflow_version, *default_reqs, "b", "-c constraints.txt"],
# ["a"],
# )
#
#
# def test_johnsnowlabs_model_save_accepts_conda_env_as_dict(jsl_model, model_path):
# conda_env = dict(mlflow.johnsnowlabs.get_default_conda_env())
# conda_env["dependencies"].append("pytest")
# mlflow.johnsnowlabs.save_model(spark_model=jsl_model, path=model_path, conda_env=conda_env)
#
# pyfunc_conf = _get_flavor_configuration(model_path=model_path, flavor_name=pyfunc.FLAVOR_NAME)
# saved_conda_env_path = Path(model_path) / pyfunc_conf[pyfunc.ENV]["conda"]
# assert os.path.exists(saved_conda_env_path)
#
# with open(saved_conda_env_path) as f:
# saved_conda_env_parsed = yaml.safe_load(f)
# assert saved_conda_env_parsed == conda_env
#
#
# def test_johnsnowlabs_model_log_persists_specified_conda_env_in_mlflow_model_directory(
# jsl_model, model_path, spark_custom_env
# ):
# artifact_path = "model"
# with mlflow.start_run():
# model_info = mlflow.johnsnowlabs.log_model(
# spark_model=jsl_model,
# artifact_path=artifact_path,
# conda_env=spark_custom_env,
# )
# model_uri = "runs:/{run_id}/{artifact_path}".format(
# run_id=mlflow.active_run().info.run_id, artifact_path=artifact_path
# )
# assert model_info.model_uri == model_uri
#
# model_path = _download_artifact_from_uri(artifact_uri=model_uri)
# pyfunc_conf = _get_flavor_configuration(model_path=model_path, flavor_name=pyfunc.FLAVOR_NAME)
# saved_conda_env_path = Path(model_path) / pyfunc_conf[pyfunc.ENV]["conda"]
# assert os.path.exists(saved_conda_env_path)
# assert saved_conda_env_path != spark_custom_env
#
# with open(spark_custom_env) as f:
# spark_custom_env_parsed = yaml.safe_load(f)
# with open(saved_conda_env_path) as f:
# saved_conda_env_parsed = yaml.safe_load(f)
# assert saved_conda_env_parsed == spark_custom_env_parsed
#
#
# def test_johnsnowlabs_model_log_persists_requirements_in_mlflow_model_directory(
# jsl_model, model_path, spark_custom_env
# ):
# artifact_path = "model"
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(
# spark_model=jsl_model,
# artifact_path=artifact_path,
# conda_env=spark_custom_env,
# )
# model_uri = "runs:/{run_id}/{artifact_path}".format(
# run_id=mlflow.active_run().info.run_id, artifact_path=artifact_path
# )
#
# model_path = _download_artifact_from_uri(artifact_uri=model_uri)
# saved_pip_req_path = Path(model_path) / "requirements.txt"
# _compare_conda_env_requirements(spark_custom_env, saved_pip_req_path)
#
#
# def test_model_save_without_specified_conda_env_uses_default_env_with_expected_dependencies(
# jsl_model, model_path
# ):
# mlflow.johnsnowlabs.save_model(spark_model=jsl_model, path=model_path)
# _assert_pip_requirements(model_path, mlflow.johnsnowlabs.get_default_pip_requirements())
#
#
# def test_model_log_without_specified_conda_env_uses_default_env_with_expected_dependencies(
# jsl_model,
# ):
# artifact_path = "model"
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(spark_model=jsl_model, artifact_path=artifact_path)
# model_uri = mlflow.get_artifact_uri(artifact_path)
#
# _assert_pip_requirements(model_uri, mlflow.johnsnowlabs.get_default_pip_requirements())
#
#
# def test_model_is_recorded_when_using_direct_save(jsl_model):
# # Patch `is_local_uri` to enforce direct model serialization to DFS
# with mock.patch("mlflow.johnsnowlabs.is_local_uri", return_value=False):
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(spark_model=jsl_model, artifact_path="model")
# current_tags = mlflow.get_run(mlflow.active_run().info.run_id).data.tags
# assert mlflow.utils.mlflow_tags.MLFLOW_LOGGED_MODELS in current_tags
#
#
# @pytest.mark.parametrize(
# (
# "artifact_uri",
# "db_runtime_version",
# "mlflowdbfs_disabled",
# "mlflowdbfs_available",
# "dbutils_available",
# "expected_uri",
# ),
# [
# (
# "dbfs:/databricks/mlflow-tracking/a/b",
# "12.0",
# "",
# True,
# True,
# "mlflowdbfs:///artifacts?run_id={}&path=/model/sparkml",
# ),
# (
# "dbfs:/databricks/mlflow-tracking/a/b",
# "12.0",
# "false",
# True,
# True,
# "mlflowdbfs:///artifacts?run_id={}&path=/model/sparkml",
# ),
# (
# "dbfs:/databricks/mlflow-tracking/a/b",
# "12.0",
# "false",
# True,
# False,
# "dbfs:/databricks/mlflow-tracking/a/b/model/sparkml",
# ),
# (
# "dbfs:/databricks/mlflow-tracking/a/b",
# "12.0",
# "",
# False,
# True,
# "dbfs:/databricks/mlflow-tracking/a/b/model/sparkml",
# ),
# (
# "dbfs:/databricks/mlflow-tracking/a/b",
# "",
# "",
# True,
# True,
# "dbfs:/databricks/mlflow-tracking/a/b/model/sparkml",
# ),
# (
# "dbfs:/databricks/mlflow-tracking/a/b",
# "12.0",
# "true",
# True,
# True,
# "dbfs:/databricks/mlflow-tracking/a/b/model/sparkml",
# ),
# ("dbfs:/root/a/b", "12.0", "", True, True, "dbfs:/root/a/b/model/sparkml"),
# ("s3://mybucket/a/b", "12.0", "", True, True, "s3://mybucket/a/b/model/sparkml"),
# ],
# )
# def test_model_logged_via_mlflowdbfs_when_appropriate(
# monkeypatch,
# jsl_model,
# artifact_uri,
# db_runtime_version,
# mlflowdbfs_disabled,
# mlflowdbfs_available,
# dbutils_available,
# expected_uri,
# ):
# def mock_spark_session_load(path):
# raise Exception("MlflowDbfsClient operation failed!")
#
# mock_spark_session = mock.Mock()
# mock_read_spark_session = mock.Mock()
# mock_read_spark_session.load = mock_spark_session_load
#
# from mlflow.utils.databricks_utils import _get_dbutils as og_getdbutils
#
# def mock_get_dbutils():
# import inspect
#
# # _get_dbutils is called during run creation and model logging; to avoid breaking run
# # creation, we only mock the output if _get_dbutils is called during spark model logging
# caller_fn_name = inspect.stack()[1].function
# if caller_fn_name == "_should_use_mlflowdbfs":
# if dbutils_available:
# return mock.Mock()
# else:
# raise Exception("dbutils not available")
# else:
# return og_getdbutils()
#
# with mock.patch(
# "mlflow.utils._spark_utils._get_active_spark_session",
# return_value=mock_spark_session,
# ), mock.patch(
# "mlflow.get_artifact_uri",
# return_value=artifact_uri,
# ), mock.patch(
# "mlflow.spark._HadoopFileSystem.is_filesystem_available",
# return_value=mlflowdbfs_available,
# ), mock.patch(
# "mlflow.utils.databricks_utils.MlflowCredentialContext", autospec=True
# ), mock.patch(
# "mlflow.utils.databricks_utils._get_dbutils",
# mock_get_dbutils,
# ), mock.patch.object(
# jsl_model, "save"
# ) as mock_save, mock.patch(
# "mlflow.models.infer_pip_requirements", return_value=[]
# ) as mock_infer:
# with mlflow.start_run():
# if db_runtime_version:
# monkeypatch.setenv("DATABRICKS_RUNTIME_VERSION", db_runtime_version)
# monkeypatch.setenv("DISABLE_MLFLOWDBFS", mlflowdbfs_disabled)
# mlflow.johnsnowlabs.log_model(spark_model=jsl_model, artifact_path="model")
# mock_save.assert_called_once_with(
# expected_uri.format(mlflow.active_run().info.run_id)
# )
#
# if expected_uri.startswith("mflowdbfs"):
# # If mlflowdbfs is used, infer_pip_requirements should load the model from the
# # remote model path instead of a local tmp path.
# assert (
# mock_infer.call_args[0][0]
# == "dbfs:/databricks/mlflow-tracking/a/b/model/sparkml"
# )
#
#
# @pytest.mark.parametrize("dummy_read_shows_mlflowdbfs_available", [True, False])
# def test_model_logging_uses_mlflowdbfs_if_appropriate_when_hdfs_check_fails(
# monkeypatch, jsl_model, dummy_read_shows_mlflowdbfs_available
# ):
# def mock_spark_session_load(path):
# if dummy_read_shows_mlflowdbfs_available:
# raise Exception("MlflowdbfsClient operation failed!")
# else:
# raise Exception("mlflowdbfs filesystem not found")
#
# mock_read_spark_session = mock.Mock()
# mock_read_spark_session.load = mock_spark_session_load
# mock_spark_session = mock.Mock()
# mock_spark_session.read = mock_read_spark_session
#
# from mlflow.utils.databricks_utils import _get_dbutils as og_getdbutils
#
# def mock_get_dbutils():
# import inspect
#
# # _get_dbutils is called during run creation and model logging; to avoid breaking run
# # creation, we only mock the output if _get_dbutils is called during spark model logging
# caller_fn_name = inspect.stack()[1].function
# if caller_fn_name == "_should_use_mlflowdbfs":
# return mock.Mock()
# else:
# return og_getdbutils()
#
# with mock.patch(
# "mlflow.utils._spark_utils._get_active_spark_session",
# return_value=mock_spark_session,
# ), mock.patch(
# "mlflow.get_artifact_uri",
# return_value="dbfs:/databricks/mlflow-tracking/a/b",
# ), mock.patch(
# "mlflow.spark._HadoopFileSystem.is_filesystem_available",
# side_effect=Exception("MlflowDbfsClient operation failed!"),
# ), mock.patch(
# "mlflow.utils.databricks_utils.MlflowCredentialContext", autospec=True
# ), mock.patch(
# "mlflow.utils.databricks_utils._get_dbutils",
# mock_get_dbutils,
# ), mock.patch.object(
# jsl_model, "save"
# ) as mock_save:
# with mlflow.start_run():
# monkeypatch.setenv("DATABRICKS_RUNTIME_VERSION", "12.0")
# mlflow.johnsnowlabs.log_model(spark_model=jsl_model, artifact_path="model")
# run_id = mlflow.active_run().info.run_id
# mock_save.assert_called_once_with(
# f"mlflowdbfs:///artifacts?run_id={run_id}&path=/model/sparkml"
# if dummy_read_shows_mlflowdbfs_available
# else "dbfs:/databricks/mlflow-tracking/a/b/model/sparkml"
# )
#
#
# def test_log_model_with_code_paths(jsl_model):
# artifact_path = "model"
# with mlflow.start_run(), mock.patch(
# "mlflow.johnsnowlabs._add_code_from_conf_to_system_path",
# wraps=_add_code_from_conf_to_system_path,
# ) as add_mock:
# mlflow.johnsnowlabs.log_model(
# spark_model=jsl_model, artifact_path=artifact_path, code_paths=[__file__]
# )
# model_uri = mlflow.get_artifact_uri(artifact_path)
# _compare_logged_code_paths(__file__, model_uri, mlflow.johnsnowlabs.FLAVOR_NAME)
# mlflow.johnsnowlabs.load_model(model_uri)
# add_mock.assert_called()
#
#
# def test_virtualenv_subfield_points_to_correct_path(jsl_model, model_path):
# mlflow.johnsnowlabs.save_model(jsl_model, path=model_path)
# pyfunc_conf = _get_flavor_configuration(model_path=model_path, flavor_name=pyfunc.FLAVOR_NAME)
# python_env_path = Path(model_path, pyfunc_conf[pyfunc.ENV]["virtualenv"])
# assert python_env_path.exists()
# assert python_env_path.is_file()
#
#
# def test_model_save_load_with_metadata(jsl_model, model_path):
# mlflow.johnsnowlabs.save_model(
# jsl_model, path=model_path, metadata={"metadata_key": "metadata_value"}
# )
#
# reloaded_model = mlflow.pyfunc.load_model(model_uri=model_path)
# assert reloaded_model.metadata.metadata["metadata_key"] == "metadata_value"
#
#
# def test_model_log_with_metadata(jsl_model):
# artifact_path = "model"
#
# with mlflow.start_run():
# mlflow.johnsnowlabs.log_model(
# jsl_model,
# artifact_path=artifact_path,
# metadata={"metadata_key": "metadata_value"},
# )
# model_uri = mlflow.get_artifact_uri(artifact_path)
#
# reloaded_model = mlflow.pyfunc.load_model(model_uri=model_uri)
# assert reloaded_model.metadata.metadata["metadata_key"] == "metadata_value"