We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Replace `pytest` with `dev/pytest.sh` in `mlflow/ml-package-versions.yml`
### Summary

A follow-up task for https://github.com/mlflow/mlflow/pull/10006. Replace `pytest` with `dev/pytest.sh` in `mlflow/ml-package-versions.yml` to enable the `fail-fast` behavior.


Example:

```diff
diff --git a/mlflow/ml-package-versions.yml b/mlflow/ml-package-versions.yml
index 4fdc2635d..373aa9e17 100644
--- a/mlflow/ml-package-versions.yml
+++ b/mlflow/ml-package-versions.yml
@@ -229,7 +229,7 @@ fastai:
       "< 2.8.0": ["torch<1.13.0", "torchvision<0.14.0"]
       ">= 2.8.0": ["torch", "torchvision"]
     run: |
-      pytest tests/fastai/test_fastai_autolog.py
+      dev/pytest.sh tests/fastai/test_fastai_autolog.py
 
 onnx:
   package_info:
```` 

### Notes

- Make sure to open a PR from a **non-master** branch.
- Sign off the commit using the `-s` flag when making a commit:

  ```sh
  git commit -s -m "..."
           # ^^ make sure to use this
  ```

- Include `Resolve #{issue_number}` (e.g. `Resolve #123`) in the PR description when opening a PR.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conftest.py`
Content:
```
1 import json
2 import os
3 import posixpath
4 import shutil
5 import subprocess
6 import sys
7 
8 import click
9 import pytest
10 
11 from mlflow.environment_variables import _MLFLOW_TESTING, MLFLOW_TRACKING_URI
12 from mlflow.version import VERSION
13 
14 from tests.helper_functions import get_safe_port
15 
16 
17 def pytest_addoption(parser):
18     parser.addoption(
19         "--requires-ssh",
20         action="store_true",
21         dest="requires_ssh",
22         default=False,
23         help="Run tests decorated with 'requires_ssh' annotation. "
24         "These tests require keys to be configured locally "
25         "for SSH authentication.",
26     )
27     parser.addoption(
28         "--ignore-flavors",
29         action="store_true",
30         dest="ignore_flavors",
31         default=False,
32         help="Ignore tests for model flavors.",
33     )
34     parser.addoption(
35         "--splits",
36         default=None,
37         type=int,
38         help="The number of groups to split tests into.",
39     )
40     parser.addoption(
41         "--group",
42         default=None,
43         type=int,
44         help="The group of tests to run.",
45     )
46     parser.addoption(
47         "--serve-wheel",
48         action="store_true",
49         default=os.getenv("CI", "false").lower() == "true",
50         help="Serve a wheel for the dev version of MLflow. True by default in CI, False otherwise.",
51     )
52 
53 
54 def pytest_configure(config):
55     # Register markers to suppress `PytestUnknownMarkWarning`
56     config.addinivalue_line("markers", "requires_ssh")
57     config.addinivalue_line("markers", "notrackingurimock")
58     config.addinivalue_line("markers", "allow_infer_pip_requirements_fallback")
59 
60 
61 @pytest.hookimpl(tryfirst=True)
62 def pytest_cmdline_main(config):
63     group = config.getoption("group")
64     splits = config.getoption("splits")
65 
66     if splits is None and group is None:
67         return None
68 
69     if splits and group is None:
70         raise pytest.UsageError("`--group` is required")
71 
72     if group and splits is None:
73         raise pytest.UsageError("`--splits` is required")
74 
75     if splits < 0:
76         raise pytest.UsageError("`--splits` must be >= 1")
77 
78     if group < 1 or group > splits:
79         raise pytest.UsageError("`--group` must be between 1 and {splits}")
80 
81     return None
82 
83 
84 def pytest_sessionstart(session):
85     if uri := MLFLOW_TRACKING_URI.get():
86         click.echo(
87             click.style(
88                 (
89                     f"Environment variable {MLFLOW_TRACKING_URI} is set to {uri!r}, "
90                     "which may interfere with tests."
91                 ),
92                 fg="red",
93             )
94         )
95 
96 
97 def pytest_runtest_setup(item):
98     markers = [mark.name for mark in item.iter_markers()]
99     if "requires_ssh" in markers and not item.config.getoption("--requires-ssh"):
100         pytest.skip("use `--requires-ssh` to run this test")
101 
102 
103 @pytest.hookimpl(hookwrapper=True)
104 def pytest_report_teststatus(report, config):
105     outcome = yield
106     if report.when == "call":
107         try:
108             import psutil
109         except ImportError:
110             return
111 
112         (*rest, result) = outcome.get_result()
113         mem = psutil.virtual_memory()
114         mem_used = mem.used / 1024**3
115         mem_total = mem.total / 1024**3
116 
117         disk = psutil.disk_usage("/")
118         disk_used = disk.used / 1024**3
119         disk_total = disk.total / 1024**3
120         outcome.force_result(
121             (
122                 *rest,
123                 (
124                     f"{result} | "
125                     f"MEM {mem_used:.1f}/{mem_total:.1f} GB | "
126                     f"DISK {disk_used:.1f}/{disk_total:.1f} GB"
127                 ),
128             )
129         )
130 
131 
132 @pytest.hookimpl(hookwrapper=True)
133 def pytest_ignore_collect(path, config):
134     outcome = yield
135     if not outcome.get_result() and config.getoption("ignore_flavors"):
136         # If not ignored by the default hook and `--ignore-flavors` specified
137 
138         # Ignored files and directories must be included in dev/run-python-flavor-tests.sh
139         model_flavors = [
140             # Tests of flavor modules.
141             "tests/azureml",
142             "tests/catboost",
143             "tests/diviner",
144             "tests/fastai",
145             "tests/gluon",
146             "tests/h2o",
147             "tests/johnsnowlabs",
148             "tests/keras",
149             "tests/keras_core",
150             "tests/langchain",
151             "tests/lightgbm",
152             "tests/mleap",
153             "tests/models",
154             "tests/onnx",
155             "tests/openai",
156             "tests/paddle",
157             "tests/pmdarima",
158             "tests/prophet",
159             "tests/pyfunc",
160             "tests/pytorch",
161             "tests/sagemaker",
162             "tests/sentence_transformers",
163             "tests/shap",
164             "tests/sklearn",
165             "tests/spacy",
166             "tests/spark",
167             "tests/statsmodels",
168             "tests/tensorflow",
169             "tests/transformers",
170             "tests/xgboost",
171             # Lazy loading test.
172             "tests/test_mlflow_lazily_imports_ml_packages.py",
173             # Tests of utils.
174             "tests/utils/test_model_utils.py",
175             # This test is included here because it imports many big libraries like tf, keras, etc.
176             "tests/tracking/fluent/test_fluent_autolog.py",
177             # Cross flavor autologging related tests.
178             "tests/autologging/test_autologging_safety_unit.py",
179             "tests/autologging/test_autologging_behaviors_unit.py",
180             "tests/autologging/test_autologging_behaviors_integration.py",
181             "tests/autologging/test_autologging_utils.py",
182             "tests/autologging/test_training_session.py",
183             # Opt in authentication feature.
184             "tests/server/auth",
185             "tests/gateway",
186         ]
187 
188         relpath = os.path.relpath(str(path))
189         relpath = relpath.replace(os.sep, posixpath.sep)  # for Windows
190 
191         if relpath in model_flavors:
192             outcome.force_result(True)
193 
194 
195 @pytest.hookimpl(trylast=True)
196 def pytest_collection_modifyitems(session, config, items):  # pylint: disable=unused-argument
197     # Executing `tests.server.test_prometheus_exporter` after `tests.server.test_handlers`
198     # results in an error because Flask >= 2.2.0 doesn't allow calling setup method such as
199     # `before_request` on the application after the first request. To avoid this issue,
200     # execute `tests.server.test_prometheus_exporter` first by reordering the test items.
201     items.sort(key=lambda item: item.module.__name__ != "tests.server.test_prometheus_exporter")
202 
203     # Select the tests to run based on the group and splits
204     if (splits := config.getoption("--splits")) and (group := config.getoption("--group")):
205         items[:] = items[(group - 1) :: splits]
206 
207 
208 @pytest.hookimpl(hookwrapper=True)
209 def pytest_terminal_summary(
210     terminalreporter, exitstatus, config
211 ):  # pylint: disable=unused-argument
212     yield
213     failed_test_reports = terminalreporter.stats.get("failed", [])
214     if failed_test_reports:
215         if len(failed_test_reports) <= 30:
216             terminalreporter.section("command to run failed test cases")
217             ids = [repr(report.nodeid) for report in failed_test_reports]
218         else:
219             terminalreporter.section("command to run failed test suites")
220             # Use dict.fromkeys to preserve the order
221             ids = list(dict.fromkeys(report.fspath for report in failed_test_reports))
222         terminalreporter.write(" ".join(["pytest"] + ids))
223         terminalreporter.write("\n" * 2)
224 
225         # If some tests failed at installing mlflow, we suggest using `--serve-wheel` flag.
226         # Some test cases try to install mlflow via pip e.g. model loading. They pins
227         # mlflow version to install based on local environment i.e. dev version ahead of
228         # the latest release, hence it's not found on PyPI. `--serve-wheel` flag was
229         # introduced to resolve this issue, which starts local PyPI server and serve
230         # an mlflow wheel based on local source code.
231         # Ref: https://github.com/mlflow/mlflow/pull/10247
232         msg = f"No matching distribution found for mlflow=={VERSION}"
233         for rep in failed_test_reports:
234             if any(msg in t for t in (rep.longreprtext, rep.capstdout, rep.capstderr)):
235                 terminalreporter.section("HINTS", yellow=True)
236                 terminalreporter.write(
237                     f"Found test(s) that failed with {msg!r}. Adding"
238                     " --serve-wheel` flag to your pytest command may help.\n\n",
239                     yellow=True,
240                 )
241                 break
242 
243 
244 @pytest.fixture(scope="module", autouse=True)
245 def clean_up_envs():
246     yield
247 
248     if "GITHUB_ACTIONS" in os.environ:
249         from mlflow.utils.virtualenv import _get_mlflow_virtualenv_root
250 
251         shutil.rmtree(_get_mlflow_virtualenv_root(), ignore_errors=True)
252         if os.name != "nt":
253             conda_info = json.loads(subprocess.check_output(["conda", "info", "--json"], text=True))
254             root_prefix = conda_info["root_prefix"]
255             for env in conda_info["envs"]:
256                 if env != root_prefix:
257                     shutil.rmtree(env, ignore_errors=True)
258 
259 
260 @pytest.fixture(scope="session", autouse=True)
261 def enable_mlflow_testing():
262     with pytest.MonkeyPatch.context() as mp:
263         mp.setenv(_MLFLOW_TESTING.name, "TRUE")
264         yield
265 
266 
267 @pytest.fixture(scope="session", autouse=True)
268 def serve_wheel(request, tmp_path_factory):
269     """
270     Models logged during tests have a dependency on the dev version of MLflow built from
271     source (e.g., mlflow==1.20.0.dev0) and cannot be served because the dev version is not
272     available on PyPI. This fixture serves a wheel for the dev version from a temporary
273     PyPI repository running on localhost and appends the repository URL to the
274     `PIP_EXTRA_INDEX_URL` environment variable to make the wheel available to pip.
275     """
276     if not request.config.getoption("--serve-wheel"):
277         yield  # pytest expects a generator fixture to yield
278         return
279 
280     root = tmp_path_factory.mktemp("root")
281     mlflow_dir = root.joinpath("mlflow")
282     mlflow_dir.mkdir()
283     port = get_safe_port()
284     try:
285         repo_root = subprocess.check_output(
286             [
287                 "git",
288                 "rev-parse",
289                 "--show-toplevel",
290             ],
291             text=True,
292         ).strip()
293     except subprocess.CalledProcessError:
294         # Some tests run in a Docker container where git is not installed.
295         # In this case, assume we're in the root of the repo.
296         repo_root = "."
297 
298     subprocess.run(
299         [
300             sys.executable,
301             "-m",
302             "pip",
303             "wheel",
304             "--wheel-dir",
305             mlflow_dir,
306             "--no-deps",
307             repo_root,
308         ],
309         check=True,
310     )
311     with subprocess.Popen(
312         [
313             sys.executable,
314             "-m",
315             "http.server",
316             str(port),
317         ],
318         cwd=root,
319     ) as prc:
320         url = f"http://localhost:{port}"
321         if existing_url := os.environ.get("PIP_EXTRA_INDEX_URL"):
322             url = f"{existing_url} {url}"
323         os.environ["PIP_EXTRA_INDEX_URL"] = url
324 
325         yield
326         prc.terminate()
327
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/conftest.py b/conftest.py
--- a/conftest.py
+++ b/conftest.py
@@ -100,6 +100,27 @@
         pytest.skip("use `--requires-ssh` to run this test")
 
 
+def fetch_pr_labels():
+    """
+    Returns the labels associated with the current pull request.
+    """
+    if "GITHUB_ACTIONS" not in os.environ:
+        return None
+
+    if os.environ.get("GITHUB_EVENT_NAME") != "pull_request":
+        return None
+
+    with open(os.environ["GITHUB_EVENT_PATH"]) as f:
+        pr_data = json.load(f)
+        return [label["name"] for label in pr_data["pull_request"]["labels"]]
+
+
+def pytest_configure(config):
+    labels = fetch_pr_labels() or []
+    if "fail-fast" in labels:
+        config.option.maxfail = 1
+
+
 @pytest.hookimpl(hookwrapper=True)
 def pytest_report_teststatus(report, config):
     outcome = yield

{"golden_diff": "diff --git a/conftest.py b/conftest.py\n--- a/conftest.py\n+++ b/conftest.py\n@@ -100,6 +100,27 @@\n         pytest.skip(\"use `--requires-ssh` to run this test\")\n \n \n+def fetch_pr_labels():\n+    \"\"\"\n+    Returns the labels associated with the current pull request.\n+    \"\"\"\n+    if \"GITHUB_ACTIONS\" not in os.environ:\n+        return None\n+\n+    if os.environ.get(\"GITHUB_EVENT_NAME\") != \"pull_request\":\n+        return None\n+\n+    with open(os.environ[\"GITHUB_EVENT_PATH\"]) as f:\n+        pr_data = json.load(f)\n+        return [label[\"name\"] for label in pr_data[\"pull_request\"][\"labels\"]]\n+\n+\n+def pytest_configure(config):\n+    labels = fetch_pr_labels() or []\n+    if \"fail-fast\" in labels:\n+        config.option.maxfail = 1\n+\n+\n @pytest.hookimpl(hookwrapper=True)\n def pytest_report_teststatus(report, config):\n     outcome = yield\n", "issue": "Replace `pytest` with `dev/pytest.sh` in `mlflow/ml-package-versions.yml`\n### Summary\r\n\r\nA follow-up task for https://github.com/mlflow/mlflow/pull/10006. Replace `pytest` with `dev/pytest.sh` in `mlflow/ml-package-versions.yml` to enable the `fail-fast` behavior.\r\n\r\n\r\nExample:\r\n\r\n```diff\r\ndiff --git a/mlflow/ml-package-versions.yml b/mlflow/ml-package-versions.yml\r\nindex 4fdc2635d..373aa9e17 100644\r\n--- a/mlflow/ml-package-versions.yml\r\n+++ b/mlflow/ml-package-versions.yml\r\n@@ -229,7 +229,7 @@ fastai:\r\n       \"< 2.8.0\": [\"torch<1.13.0\", \"torchvision<0.14.0\"]\r\n       \">= 2.8.0\": [\"torch\", \"torchvision\"]\r\n     run: |\r\n-      pytest tests/fastai/test_fastai_autolog.py\r\n+      dev/pytest.sh tests/fastai/test_fastai_autolog.py\r\n \r\n onnx:\r\n   package_info:\r\n```` \r\n\r\n### Notes\r\n\r\n- Make sure to open a PR from a **non-master** branch.\r\n- Sign off the commit using the `-s` flag when making a commit:\r\n\r\n  ```sh\r\n  git commit -s -m \"...\"\r\n           # ^^ make sure to use this\r\n  ```\r\n\r\n- Include `Resolve #{issue_number}` (e.g. `Resolve #123`) in the PR description when opening a PR.\r\n\n", "before_files": [{"content": "import json\nimport os\nimport posixpath\nimport shutil\nimport subprocess\nimport sys\n\nimport click\nimport pytest\n\nfrom mlflow.environment_variables import _MLFLOW_TESTING, MLFLOW_TRACKING_URI\nfrom mlflow.version import VERSION\n\nfrom tests.helper_functions import get_safe_port\n\n\ndef pytest_addoption(parser):\n    parser.addoption(\n        \"--requires-ssh\",\n        action=\"store_true\",\n        dest=\"requires_ssh\",\n        default=False,\n        help=\"Run tests decorated with 'requires_ssh' annotation. \"\n        \"These tests require keys to be configured locally \"\n        \"for SSH authentication.\",\n    )\n    parser.addoption(\n        \"--ignore-flavors\",\n        action=\"store_true\",\n        dest=\"ignore_flavors\",\n        default=False,\n        help=\"Ignore tests for model flavors.\",\n    )\n    parser.addoption(\n        \"--splits\",\n        default=None,\n        type=int,\n        help=\"The number of groups to split tests into.\",\n    )\n    parser.addoption(\n        \"--group\",\n        default=None,\n        type=int,\n        help=\"The group of tests to run.\",\n    )\n    parser.addoption(\n        \"--serve-wheel\",\n        action=\"store_true\",\n        default=os.getenv(\"CI\", \"false\").lower() == \"true\",\n        help=\"Serve a wheel for the dev version of MLflow. True by default in CI, False otherwise.\",\n    )\n\n\ndef pytest_configure(config):\n    # Register markers to suppress `PytestUnknownMarkWarning`\n    config.addinivalue_line(\"markers\", \"requires_ssh\")\n    config.addinivalue_line(\"markers\", \"notrackingurimock\")\n    config.addinivalue_line(\"markers\", \"allow_infer_pip_requirements_fallback\")\n\n\n@pytest.hookimpl(tryfirst=True)\ndef pytest_cmdline_main(config):\n    group = config.getoption(\"group\")\n    splits = config.getoption(\"splits\")\n\n    if splits is None and group is None:\n        return None\n\n    if splits and group is None:\n        raise pytest.UsageError(\"`--group` is required\")\n\n    if group and splits is None:\n        raise pytest.UsageError(\"`--splits` is required\")\n\n    if splits < 0:\n        raise pytest.UsageError(\"`--splits` must be >= 1\")\n\n    if group < 1 or group > splits:\n        raise pytest.UsageError(\"`--group` must be between 1 and {splits}\")\n\n    return None\n\n\ndef pytest_sessionstart(session):\n    if uri := MLFLOW_TRACKING_URI.get():\n        click.echo(\n            click.style(\n                (\n                    f\"Environment variable {MLFLOW_TRACKING_URI} is set to {uri!r}, \"\n                    \"which may interfere with tests.\"\n                ),\n                fg=\"red\",\n            )\n        )\n\n\ndef pytest_runtest_setup(item):\n    markers = [mark.name for mark in item.iter_markers()]\n    if \"requires_ssh\" in markers and not item.config.getoption(\"--requires-ssh\"):\n        pytest.skip(\"use `--requires-ssh` to run this test\")\n\n\n@pytest.hookimpl(hookwrapper=True)\ndef pytest_report_teststatus(report, config):\n    outcome = yield\n    if report.when == \"call\":\n        try:\n            import psutil\n        except ImportError:\n            return\n\n        (*rest, result) = outcome.get_result()\n        mem = psutil.virtual_memory()\n        mem_used = mem.used / 1024**3\n        mem_total = mem.total / 1024**3\n\n        disk = psutil.disk_usage(\"/\")\n        disk_used = disk.used / 1024**3\n        disk_total = disk.total / 1024**3\n        outcome.force_result(\n            (\n                *rest,\n                (\n                    f\"{result} | \"\n                    f\"MEM {mem_used:.1f}/{mem_total:.1f} GB | \"\n                    f\"DISK {disk_used:.1f}/{disk_total:.1f} GB\"\n                ),\n            )\n        )\n\n\n@pytest.hookimpl(hookwrapper=True)\ndef pytest_ignore_collect(path, config):\n    outcome = yield\n    if not outcome.get_result() and config.getoption(\"ignore_flavors\"):\n        # If not ignored by the default hook and `--ignore-flavors` specified\n\n        # Ignored files and directories must be included in dev/run-python-flavor-tests.sh\n        model_flavors = [\n            # Tests of flavor modules.\n            \"tests/azureml\",\n            \"tests/catboost\",\n            \"tests/diviner\",\n            \"tests/fastai\",\n            \"tests/gluon\",\n            \"tests/h2o\",\n            \"tests/johnsnowlabs\",\n            \"tests/keras\",\n            \"tests/keras_core\",\n            \"tests/langchain\",\n            \"tests/lightgbm\",\n            \"tests/mleap\",\n            \"tests/models\",\n            \"tests/onnx\",\n            \"tests/openai\",\n            \"tests/paddle\",\n            \"tests/pmdarima\",\n            \"tests/prophet\",\n            \"tests/pyfunc\",\n            \"tests/pytorch\",\n            \"tests/sagemaker\",\n            \"tests/sentence_transformers\",\n            \"tests/shap\",\n            \"tests/sklearn\",\n            \"tests/spacy\",\n            \"tests/spark\",\n            \"tests/statsmodels\",\n            \"tests/tensorflow\",\n            \"tests/transformers\",\n            \"tests/xgboost\",\n            # Lazy loading test.\n            \"tests/test_mlflow_lazily_imports_ml_packages.py\",\n            # Tests of utils.\n            \"tests/utils/test_model_utils.py\",\n            # This test is included here because it imports many big libraries like tf, keras, etc.\n            \"tests/tracking/fluent/test_fluent_autolog.py\",\n            # Cross flavor autologging related tests.\n            \"tests/autologging/test_autologging_safety_unit.py\",\n            \"tests/autologging/test_autologging_behaviors_unit.py\",\n            \"tests/autologging/test_autologging_behaviors_integration.py\",\n            \"tests/autologging/test_autologging_utils.py\",\n            \"tests/autologging/test_training_session.py\",\n            # Opt in authentication feature.\n            \"tests/server/auth\",\n            \"tests/gateway\",\n        ]\n\n        relpath = os.path.relpath(str(path))\n        relpath = relpath.replace(os.sep, posixpath.sep)  # for Windows\n\n        if relpath in model_flavors:\n            outcome.force_result(True)\n\n\n@pytest.hookimpl(trylast=True)\ndef pytest_collection_modifyitems(session, config, items):  # pylint: disable=unused-argument\n    # Executing `tests.server.test_prometheus_exporter` after `tests.server.test_handlers`\n    # results in an error because Flask >= 2.2.0 doesn't allow calling setup method such as\n    # `before_request` on the application after the first request. To avoid this issue,\n    # execute `tests.server.test_prometheus_exporter` first by reordering the test items.\n    items.sort(key=lambda item: item.module.__name__ != \"tests.server.test_prometheus_exporter\")\n\n    # Select the tests to run based on the group and splits\n    if (splits := config.getoption(\"--splits\")) and (group := config.getoption(\"--group\")):\n        items[:] = items[(group - 1) :: splits]\n\n\n@pytest.hookimpl(hookwrapper=True)\ndef pytest_terminal_summary(\n    terminalreporter, exitstatus, config\n):  # pylint: disable=unused-argument\n    yield\n    failed_test_reports = terminalreporter.stats.get(\"failed\", [])\n    if failed_test_reports:\n        if len(failed_test_reports) <= 30:\n            terminalreporter.section(\"command to run failed test cases\")\n            ids = [repr(report.nodeid) for report in failed_test_reports]\n        else:\n            terminalreporter.section(\"command to run failed test suites\")\n            # Use dict.fromkeys to preserve the order\n            ids = list(dict.fromkeys(report.fspath for report in failed_test_reports))\n        terminalreporter.write(\" \".join([\"pytest\"] + ids))\n        terminalreporter.write(\"\\n\" * 2)\n\n        # If some tests failed at installing mlflow, we suggest using `--serve-wheel` flag.\n        # Some test cases try to install mlflow via pip e.g. model loading. They pins\n        # mlflow version to install based on local environment i.e. dev version ahead of\n        # the latest release, hence it's not found on PyPI. `--serve-wheel` flag was\n        # introduced to resolve this issue, which starts local PyPI server and serve\n        # an mlflow wheel based on local source code.\n        # Ref: https://github.com/mlflow/mlflow/pull/10247\n        msg = f\"No matching distribution found for mlflow=={VERSION}\"\n        for rep in failed_test_reports:\n            if any(msg in t for t in (rep.longreprtext, rep.capstdout, rep.capstderr)):\n                terminalreporter.section(\"HINTS\", yellow=True)\n                terminalreporter.write(\n                    f\"Found test(s) that failed with {msg!r}. Adding\"\n                    \" --serve-wheel` flag to your pytest command may help.\\n\\n\",\n                    yellow=True,\n                )\n                break\n\n\n@pytest.fixture(scope=\"module\", autouse=True)\ndef clean_up_envs():\n    yield\n\n    if \"GITHUB_ACTIONS\" in os.environ:\n        from mlflow.utils.virtualenv import _get_mlflow_virtualenv_root\n\n        shutil.rmtree(_get_mlflow_virtualenv_root(), ignore_errors=True)\n        if os.name != \"nt\":\n            conda_info = json.loads(subprocess.check_output([\"conda\", \"info\", \"--json\"], text=True))\n            root_prefix = conda_info[\"root_prefix\"]\n            for env in conda_info[\"envs\"]:\n                if env != root_prefix:\n                    shutil.rmtree(env, ignore_errors=True)\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef enable_mlflow_testing():\n    with pytest.MonkeyPatch.context() as mp:\n        mp.setenv(_MLFLOW_TESTING.name, \"TRUE\")\n        yield\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef serve_wheel(request, tmp_path_factory):\n    \"\"\"\n    Models logged during tests have a dependency on the dev version of MLflow built from\n    source (e.g., mlflow==1.20.0.dev0) and cannot be served because the dev version is not\n    available on PyPI. This fixture serves a wheel for the dev version from a temporary\n    PyPI repository running on localhost and appends the repository URL to the\n    `PIP_EXTRA_INDEX_URL` environment variable to make the wheel available to pip.\n    \"\"\"\n    if not request.config.getoption(\"--serve-wheel\"):\n        yield  # pytest expects a generator fixture to yield\n        return\n\n    root = tmp_path_factory.mktemp(\"root\")\n    mlflow_dir = root.joinpath(\"mlflow\")\n    mlflow_dir.mkdir()\n    port = get_safe_port()\n    try:\n        repo_root = subprocess.check_output(\n            [\n                \"git\",\n                \"rev-parse\",\n                \"--show-toplevel\",\n            ],\n            text=True,\n        ).strip()\n    except subprocess.CalledProcessError:\n        # Some tests run in a Docker container where git is not installed.\n        # In this case, assume we're in the root of the repo.\n        repo_root = \".\"\n\n    subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"pip\",\n            \"wheel\",\n            \"--wheel-dir\",\n            mlflow_dir,\n            \"--no-deps\",\n            repo_root,\n        ],\n        check=True,\n    )\n    with subprocess.Popen(\n        [\n            sys.executable,\n            \"-m\",\n            \"http.server\",\n            str(port),\n        ],\n        cwd=root,\n    ) as prc:\n        url = f\"http://localhost:{port}\"\n        if existing_url := os.environ.get(\"PIP_EXTRA_INDEX_URL\"):\n            url = f\"{existing_url} {url}\"\n        os.environ[\"PIP_EXTRA_INDEX_URL\"] = url\n\n        yield\n        prc.terminate()\n", "path": "conftest.py"}], "after_files": [{"content": "import json\nimport os\nimport posixpath\nimport shutil\nimport subprocess\nimport sys\n\nimport click\nimport pytest\n\nfrom mlflow.environment_variables import _MLFLOW_TESTING, MLFLOW_TRACKING_URI\nfrom mlflow.version import VERSION\n\nfrom tests.helper_functions import get_safe_port\n\n\ndef pytest_addoption(parser):\n    parser.addoption(\n        \"--requires-ssh\",\n        action=\"store_true\",\n        dest=\"requires_ssh\",\n        default=False,\n        help=\"Run tests decorated with 'requires_ssh' annotation. \"\n        \"These tests require keys to be configured locally \"\n        \"for SSH authentication.\",\n    )\n    parser.addoption(\n        \"--ignore-flavors\",\n        action=\"store_true\",\n        dest=\"ignore_flavors\",\n        default=False,\n        help=\"Ignore tests for model flavors.\",\n    )\n    parser.addoption(\n        \"--splits\",\n        default=None,\n        type=int,\n        help=\"The number of groups to split tests into.\",\n    )\n    parser.addoption(\n        \"--group\",\n        default=None,\n        type=int,\n        help=\"The group of tests to run.\",\n    )\n    parser.addoption(\n        \"--serve-wheel\",\n        action=\"store_true\",\n        default=os.getenv(\"CI\", \"false\").lower() == \"true\",\n        help=\"Serve a wheel for the dev version of MLflow. True by default in CI, False otherwise.\",\n    )\n\n\ndef pytest_configure(config):\n    # Register markers to suppress `PytestUnknownMarkWarning`\n    config.addinivalue_line(\"markers\", \"requires_ssh\")\n    config.addinivalue_line(\"markers\", \"notrackingurimock\")\n    config.addinivalue_line(\"markers\", \"allow_infer_pip_requirements_fallback\")\n\n\n@pytest.hookimpl(tryfirst=True)\ndef pytest_cmdline_main(config):\n    group = config.getoption(\"group\")\n    splits = config.getoption(\"splits\")\n\n    if splits is None and group is None:\n        return None\n\n    if splits and group is None:\n        raise pytest.UsageError(\"`--group` is required\")\n\n    if group and splits is None:\n        raise pytest.UsageError(\"`--splits` is required\")\n\n    if splits < 0:\n        raise pytest.UsageError(\"`--splits` must be >= 1\")\n\n    if group < 1 or group > splits:\n        raise pytest.UsageError(\"`--group` must be between 1 and {splits}\")\n\n    return None\n\n\ndef pytest_sessionstart(session):\n    if uri := MLFLOW_TRACKING_URI.get():\n        click.echo(\n            click.style(\n                (\n                    f\"Environment variable {MLFLOW_TRACKING_URI} is set to {uri!r}, \"\n                    \"which may interfere with tests.\"\n                ),\n                fg=\"red\",\n            )\n        )\n\n\ndef pytest_runtest_setup(item):\n    markers = [mark.name for mark in item.iter_markers()]\n    if \"requires_ssh\" in markers and not item.config.getoption(\"--requires-ssh\"):\n        pytest.skip(\"use `--requires-ssh` to run this test\")\n\n\ndef fetch_pr_labels():\n    \"\"\"\n    Returns the labels associated with the current pull request.\n    \"\"\"\n    if \"GITHUB_ACTIONS\" not in os.environ:\n        return None\n\n    if os.environ.get(\"GITHUB_EVENT_NAME\") != \"pull_request\":\n        return None\n\n    with open(os.environ[\"GITHUB_EVENT_PATH\"]) as f:\n        pr_data = json.load(f)\n        return [label[\"name\"] for label in pr_data[\"pull_request\"][\"labels\"]]\n\n\ndef pytest_configure(config):\n    labels = fetch_pr_labels() or []\n    if \"fail-fast\" in labels:\n        config.option.maxfail = 1\n\n\n@pytest.hookimpl(hookwrapper=True)\ndef pytest_report_teststatus(report, config):\n    outcome = yield\n    if report.when == \"call\":\n        try:\n            import psutil\n        except ImportError:\n            return\n\n        (*rest, result) = outcome.get_result()\n        mem = psutil.virtual_memory()\n        mem_used = mem.used / 1024**3\n        mem_total = mem.total / 1024**3\n\n        disk = psutil.disk_usage(\"/\")\n        disk_used = disk.used / 1024**3\n        disk_total = disk.total / 1024**3\n        outcome.force_result(\n            (\n                *rest,\n                (\n                    f\"{result} | \"\n                    f\"MEM {mem_used:.1f}/{mem_total:.1f} GB | \"\n                    f\"DISK {disk_used:.1f}/{disk_total:.1f} GB\"\n                ),\n            )\n        )\n\n\n@pytest.hookimpl(hookwrapper=True)\ndef pytest_ignore_collect(path, config):\n    outcome = yield\n    if not outcome.get_result() and config.getoption(\"ignore_flavors\"):\n        # If not ignored by the default hook and `--ignore-flavors` specified\n\n        # Ignored files and directories must be included in dev/run-python-flavor-tests.sh\n        model_flavors = [\n            # Tests of flavor modules.\n            \"tests/azureml\",\n            \"tests/catboost\",\n            \"tests/diviner\",\n            \"tests/fastai\",\n            \"tests/gluon\",\n            \"tests/h2o\",\n            \"tests/johnsnowlabs\",\n            \"tests/keras\",\n            \"tests/keras_core\",\n            \"tests/langchain\",\n            \"tests/lightgbm\",\n            \"tests/mleap\",\n            \"tests/models\",\n            \"tests/onnx\",\n            \"tests/openai\",\n            \"tests/paddle\",\n            \"tests/pmdarima\",\n            \"tests/prophet\",\n            \"tests/pyfunc\",\n            \"tests/pytorch\",\n            \"tests/sagemaker\",\n            \"tests/sentence_transformers\",\n            \"tests/shap\",\n            \"tests/sklearn\",\n            \"tests/spacy\",\n            \"tests/spark\",\n            \"tests/statsmodels\",\n            \"tests/tensorflow\",\n            \"tests/transformers\",\n            \"tests/xgboost\",\n            # Lazy loading test.\n            \"tests/test_mlflow_lazily_imports_ml_packages.py\",\n            # Tests of utils.\n            \"tests/utils/test_model_utils.py\",\n            # This test is included here because it imports many big libraries like tf, keras, etc.\n            \"tests/tracking/fluent/test_fluent_autolog.py\",\n            # Cross flavor autologging related tests.\n            \"tests/autologging/test_autologging_safety_unit.py\",\n            \"tests/autologging/test_autologging_behaviors_unit.py\",\n            \"tests/autologging/test_autologging_behaviors_integration.py\",\n            \"tests/autologging/test_autologging_utils.py\",\n            \"tests/autologging/test_training_session.py\",\n            # Opt in authentication feature.\n            \"tests/server/auth\",\n            \"tests/gateway\",\n        ]\n\n        relpath = os.path.relpath(str(path))\n        relpath = relpath.replace(os.sep, posixpath.sep)  # for Windows\n\n        if relpath in model_flavors:\n            outcome.force_result(True)\n\n\n@pytest.hookimpl(trylast=True)\ndef pytest_collection_modifyitems(session, config, items):  # pylint: disable=unused-argument\n    # Executing `tests.server.test_prometheus_exporter` after `tests.server.test_handlers`\n    # results in an error because Flask >= 2.2.0 doesn't allow calling setup method such as\n    # `before_request` on the application after the first request. To avoid this issue,\n    # execute `tests.server.test_prometheus_exporter` first by reordering the test items.\n    items.sort(key=lambda item: item.module.__name__ != \"tests.server.test_prometheus_exporter\")\n\n    # Select the tests to run based on the group and splits\n    if (splits := config.getoption(\"--splits\")) and (group := config.getoption(\"--group\")):\n        items[:] = items[(group - 1) :: splits]\n\n\n@pytest.hookimpl(hookwrapper=True)\ndef pytest_terminal_summary(\n    terminalreporter, exitstatus, config\n):  # pylint: disable=unused-argument\n    yield\n    failed_test_reports = terminalreporter.stats.get(\"failed\", [])\n    if failed_test_reports:\n        if len(failed_test_reports) <= 30:\n            terminalreporter.section(\"command to run failed test cases\")\n            ids = [repr(report.nodeid) for report in failed_test_reports]\n        else:\n            terminalreporter.section(\"command to run failed test suites\")\n            # Use dict.fromkeys to preserve the order\n            ids = list(dict.fromkeys(report.fspath for report in failed_test_reports))\n        terminalreporter.write(\" \".join([\"pytest\"] + ids))\n        terminalreporter.write(\"\\n\" * 2)\n\n        # If some tests failed at installing mlflow, we suggest using `--serve-wheel` flag.\n        # Some test cases try to install mlflow via pip e.g. model loading. They pins\n        # mlflow version to install based on local environment i.e. dev version ahead of\n        # the latest release, hence it's not found on PyPI. `--serve-wheel` flag was\n        # introduced to resolve this issue, which starts local PyPI server and serve\n        # an mlflow wheel based on local source code.\n        # Ref: https://github.com/mlflow/mlflow/pull/10247\n        msg = f\"No matching distribution found for mlflow=={VERSION}\"\n        for rep in failed_test_reports:\n            if any(msg in t for t in (rep.longreprtext, rep.capstdout, rep.capstderr)):\n                terminalreporter.section(\"HINTS\", yellow=True)\n                terminalreporter.write(\n                    f\"Found test(s) that failed with {msg!r}. Adding\"\n                    \" --serve-wheel` flag to your pytest command may help.\\n\\n\",\n                    yellow=True,\n                )\n                break\n\n\n@pytest.fixture(scope=\"module\", autouse=True)\ndef clean_up_envs():\n    yield\n\n    if \"GITHUB_ACTIONS\" in os.environ:\n        from mlflow.utils.virtualenv import _get_mlflow_virtualenv_root\n\n        shutil.rmtree(_get_mlflow_virtualenv_root(), ignore_errors=True)\n        if os.name != \"nt\":\n            conda_info = json.loads(subprocess.check_output([\"conda\", \"info\", \"--json\"], text=True))\n            root_prefix = conda_info[\"root_prefix\"]\n            for env in conda_info[\"envs\"]:\n                if env != root_prefix:\n                    shutil.rmtree(env, ignore_errors=True)\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef enable_mlflow_testing():\n    with pytest.MonkeyPatch.context() as mp:\n        mp.setenv(_MLFLOW_TESTING.name, \"TRUE\")\n        yield\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef serve_wheel(request, tmp_path_factory):\n    \"\"\"\n    Models logged during tests have a dependency on the dev version of MLflow built from\n    source (e.g., mlflow==1.20.0.dev0) and cannot be served because the dev version is not\n    available on PyPI. This fixture serves a wheel for the dev version from a temporary\n    PyPI repository running on localhost and appends the repository URL to the\n    `PIP_EXTRA_INDEX_URL` environment variable to make the wheel available to pip.\n    \"\"\"\n    if not request.config.getoption(\"--serve-wheel\"):\n        yield  # pytest expects a generator fixture to yield\n        return\n\n    root = tmp_path_factory.mktemp(\"root\")\n    mlflow_dir = root.joinpath(\"mlflow\")\n    mlflow_dir.mkdir()\n    port = get_safe_port()\n    try:\n        repo_root = subprocess.check_output(\n            [\n                \"git\",\n                \"rev-parse\",\n                \"--show-toplevel\",\n            ],\n            text=True,\n        ).strip()\n    except subprocess.CalledProcessError:\n        # Some tests run in a Docker container where git is not installed.\n        # In this case, assume we're in the root of the repo.\n        repo_root = \".\"\n\n    subprocess.run(\n        [\n            sys.executable,\n            \"-m\",\n            \"pip\",\n            \"wheel\",\n            \"--wheel-dir\",\n            mlflow_dir,\n            \"--no-deps\",\n            repo_root,\n        ],\n        check=True,\n    )\n    with subprocess.Popen(\n        [\n            sys.executable,\n            \"-m\",\n            \"http.server\",\n            str(port),\n        ],\n        cwd=root,\n    ) as prc:\n        url = f\"http://localhost:{port}\"\n        if existing_url := os.environ.get(\"PIP_EXTRA_INDEX_URL\"):\n            url = f\"{existing_url} {url}\"\n        os.environ[\"PIP_EXTRA_INDEX_URL\"] = url\n\n        yield\n        prc.terminate()\n", "path": "conftest.py"}]}