Skip to content

Commit 2daf8a9

Browse files
committed
Derive artifact location from MLFLOW_TRACKING_URI env var too
When MLFLOW_TRACKING_URI was set as an environment variable (not the tracking_uri argument) to a local SQLite database, the handler did not co-locate run artifacts next to the db file, so they went to MLflow's default mlruns directory relative to the working directory. An argument-passed SQLite URI did get a sibling mlruns. Resolve an effective tracking URI from the argument or the env var and key the artifact-location logic off it, while still passing only tracking_uri to the client so MLflow keeps resolving the env var. Add a regression test for the env-var SQLite case. Signed-off-by: Lawson Darrow <lawson.darrow@gmail.com>
1 parent e6c76e0 commit 2daf8a9

2 files changed

Lines changed: 38 additions & 11 deletions

File tree

monai/handlers/mlflow_handler.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,10 @@ class MLFlowHandler:
130130
workflow, default to `'lr'`.
131131
close_on_complete: whether to close the mlflow run in `complete` phase in workflow, default to False.
132132
artifact_location: the location to store run artifacts in, passed to MLflow when the experiment is
133-
created. When ``None`` and a local SQLite ``tracking_uri`` is used, it defaults to an
134-
``mlruns`` directory next to the database file; for other backends ``None`` lets MLflow
135-
decide based on the ``tracking_uri``. Has no effect if the experiment already exists.
133+
created. When ``None`` and a local SQLite backend is used (from the ``tracking_uri`` argument
134+
or the ``MLFLOW_TRACKING_URI`` environment variable), it defaults to an ``mlruns`` directory
135+
next to the database file; for other backends ``None`` lets MLflow decide based on the
136+
``tracking_uri``. Has no effect if the experiment already exists.
136137
137138
For more details of MLFlow usage, please refer to: https://mlflow.org/docs/latest/index.html.
138139
@@ -184,16 +185,23 @@ def __init__(
184185
# the `./mlruns` directory (where the previous file store default kept them) via the
185186
# experiment `artifact_location`. Any explicitly provided tracking_uri is left unchanged.
186187
self.artifact_location = artifact_location
187-
# Only fall back to the SQLite default when the caller gave no tracking_uri and the
188-
# `MLFLOW_TRACKING_URI` environment variable is unset, so that env-var configuration keeps
189-
# working. When it is set, `tracking_uri` stays None and MLflow resolves the env var.
190-
if not tracking_uri and not os.environ.get("MLFLOW_TRACKING_URI"):
191-
tracking_uri = path_to_sqlite_uri(os.path.join(os.getcwd(), "mlruns.db"))
188+
# Resolve the effective tracking URI from the argument or the `MLFLOW_TRACKING_URI`
189+
# environment variable, so both configure the artifact location the same way.
190+
effective_tracking_uri = tracking_uri or os.environ.get("MLFLOW_TRACKING_URI")
191+
# When neither is set, fall back to the local SQLite default described above.
192+
if not effective_tracking_uri:
193+
tracking_uri = effective_tracking_uri = path_to_sqlite_uri(os.path.join(os.getcwd(), "mlruns.db"))
192194
# For a local SQLite backend, keep run artifacts in an `mlruns` directory next to the
193195
# database file (mirroring the previous file-store layout) unless the caller set
194196
# `artifact_location`. Other backends (e.g. a remote server) are left to MLflow to decide.
195-
if self.artifact_location is None and tracking_uri and tracking_uri.startswith("sqlite:///"):
196-
db_path = Path(tracking_uri[len("sqlite:///") :])
197+
# Only `tracking_uri` is passed to the client, so an `MLFLOW_TRACKING_URI` env var is
198+
# still resolved by MLflow itself.
199+
if (
200+
self.artifact_location is None
201+
and effective_tracking_uri
202+
and effective_tracking_uri.startswith("sqlite:///")
203+
):
204+
db_path = Path(effective_tracking_uri[len("sqlite:///") :])
197205
self.artifact_location = path_to_uri(db_path.parent / "mlruns")
198206
self.client = mlflow.MlflowClient(tracking_uri=tracking_uri if tracking_uri else None)
199207
self.run_finish_status = mlflow.entities.RunStatus.to_string(mlflow.entities.RunStatus.FINISHED)

tests/handlers/test_handler_mlflow.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import tempfile
1818
import unittest
1919
from concurrent.futures import ThreadPoolExecutor
20-
from unittest.mock import MagicMock
20+
from unittest.mock import MagicMock, patch
2121

2222
import numpy as np
2323
from ignite.engine import Engine, Events
@@ -289,6 +289,25 @@ def test_explicit_sqlite_tracking_uri_colocates_artifacts(self):
289289
finally:
290290
handler.close() # release the SQLite handle so Windows can delete the db
291291

292+
def test_env_var_sqlite_tracking_uri_colocates_artifacts(self):
293+
# a SQLite `MLFLOW_TRACKING_URI` env var should co-locate artifacts next to the db, the
294+
# same as an explicit `tracking_uri` argument. The env var itself is left for MLflow to
295+
# resolve, so the handler does not pass it to the client.
296+
with tempfile.TemporaryDirectory() as tempdir:
297+
uri = path_to_sqlite_uri(os.path.join(tempdir, "sub", "mlruns.db"))
298+
handler = None
299+
with patch.dict(os.environ, {"MLFLOW_TRACKING_URI": uri}):
300+
try:
301+
handler = MLFlowHandler(iteration_log=False, epoch_log=False)
302+
self.assertTrue(handler.client.tracking_uri.endswith("mlruns.db"))
303+
self.assertIsNotNone(handler.artifact_location)
304+
self.assertTrue(handler.artifact_location.endswith("mlruns"))
305+
# co-located with the db file (the `sub` dir), not a cwd-relative `./mlruns`
306+
self.assertIn("sub", handler.artifact_location)
307+
finally:
308+
if handler is not None:
309+
handler.close() # release the SQLite handle so Windows can delete the db
310+
292311
def test_explicit_artifact_location_is_used(self):
293312
# an explicitly provided artifact_location should be kept even with the default SQLite
294313
# backend, so callers (e.g. the bundle defaults) can co-locate artifacts with the db.

0 commit comments

Comments
 (0)