We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support Flask 3.0 in MLflow
### Summary

We currently pin `Flask` to `< 3.0`. Flask 3.0 was recently released. We should update the requirement:

```diff
diff --git a/requirements/core-requirements.txt b/requirements/core-requirements.txt
index 99f4cdd62..11f7e3770 100644
--- a/requirements/core-requirements.txt
+++ b/requirements/core-requirements.txt
@@ -4,7 +4,7 @@
 
 alembic<2,!=1.10.0
 docker<7,>=4.0.0
-Flask<3
+Flask<4
 numpy<2
 scipy<2
 pandas<3
diff --git a/requirements/core-requirements.yaml b/requirements/core-requirements.yaml
index 5447b07c7..4ffb44c6b 100644
--- a/requirements/core-requirements.yaml
+++ b/requirements/core-requirements.yaml
@@ -17,7 +17,7 @@ docker:
 
 flask:
   pip_release: Flask
-  max_major_version: 2
+  max_major_version: 3
 
 numpy:
   pip_release: numpy
```

References:

- https://pypi.org/project/Flask/3.0.0/

### 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 `#{issue_number}` (e.g. `#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: `mlflow/server/__init__.py`
Content:
```
1 import importlib
2 import importlib.metadata
3 import os
4 import shlex
5 import sys
6 import textwrap
7 import types
8 
9 from flask import Flask, Response, send_from_directory
10 from flask import __version__ as flask_version
11 from packaging.version import Version
12 
13 from mlflow.exceptions import MlflowException
14 from mlflow.server import handlers
15 from mlflow.server.handlers import (
16     STATIC_PREFIX_ENV_VAR,
17     _add_static_prefix,
18     create_promptlab_run_handler,
19     gateway_proxy_handler,
20     get_artifact_handler,
21     get_metric_history_bulk_handler,
22     get_model_version_artifact_handler,
23     search_datasets_handler,
24     upload_artifact_handler,
25 )
26 from mlflow.utils.os import is_windows
27 from mlflow.utils.process import _exec_cmd
28 from mlflow.version import VERSION
29 
30 # NB: These are internal environment variables used for communication between
31 # the cli and the forked gunicorn processes.
32 BACKEND_STORE_URI_ENV_VAR = "_MLFLOW_SERVER_FILE_STORE"
33 REGISTRY_STORE_URI_ENV_VAR = "_MLFLOW_SERVER_REGISTRY_STORE"
34 ARTIFACT_ROOT_ENV_VAR = "_MLFLOW_SERVER_ARTIFACT_ROOT"
35 ARTIFACTS_DESTINATION_ENV_VAR = "_MLFLOW_SERVER_ARTIFACT_DESTINATION"
36 PROMETHEUS_EXPORTER_ENV_VAR = "prometheus_multiproc_dir"
37 SERVE_ARTIFACTS_ENV_VAR = "_MLFLOW_SERVER_SERVE_ARTIFACTS"
38 ARTIFACTS_ONLY_ENV_VAR = "_MLFLOW_SERVER_ARTIFACTS_ONLY"
39 
40 REL_STATIC_DIR = "js/build"
41 
42 app = Flask(__name__, static_folder=REL_STATIC_DIR)
43 IS_FLASK_V1 = Version(flask_version) < Version("2.0")
44 
45 
46 for http_path, handler, methods in handlers.get_endpoints():
47     app.add_url_rule(http_path, handler.__name__, handler, methods=methods)
48 
49 if os.getenv(PROMETHEUS_EXPORTER_ENV_VAR):
50     from mlflow.server.prometheus_exporter import activate_prometheus_exporter
51 
52     prometheus_metrics_path = os.getenv(PROMETHEUS_EXPORTER_ENV_VAR)
53     if not os.path.exists(prometheus_metrics_path):
54         os.makedirs(prometheus_metrics_path)
55     activate_prometheus_exporter(app)
56 
57 
58 # Provide a health check endpoint to ensure the application is responsive
59 @app.route("/health")
60 def health():
61     return "OK", 200
62 
63 
64 # Provide an endpoint to query the version of mlflow running on the server
65 @app.route("/version")
66 def version():
67     return VERSION, 200
68 
69 
70 # Serve the "get-artifact" route.
71 @app.route(_add_static_prefix("/get-artifact"))
72 def serve_artifacts():
73     return get_artifact_handler()
74 
75 
76 # Serve the "model-versions/get-artifact" route.
77 @app.route(_add_static_prefix("/model-versions/get-artifact"))
78 def serve_model_version_artifact():
79     return get_model_version_artifact_handler()
80 
81 
82 # Serve the "metrics/get-history-bulk" route.
83 @app.route(_add_static_prefix("/ajax-api/2.0/mlflow/metrics/get-history-bulk"))
84 def serve_get_metric_history_bulk():
85     return get_metric_history_bulk_handler()
86 
87 
88 # Serve the "experiments/search-datasets" route.
89 @app.route(_add_static_prefix("/ajax-api/2.0/mlflow/experiments/search-datasets"), methods=["POST"])
90 def serve_search_datasets():
91     return search_datasets_handler()
92 
93 
94 # Serve the "runs/create-promptlab-run" route.
95 @app.route(_add_static_prefix("/ajax-api/2.0/mlflow/runs/create-promptlab-run"), methods=["POST"])
96 def serve_create_promptlab_run():
97     return create_promptlab_run_handler()
98 
99 
100 @app.route(_add_static_prefix("/ajax-api/2.0/mlflow/gateway-proxy"), methods=["POST", "GET"])
101 def serve_gateway_proxy():
102     return gateway_proxy_handler()
103 
104 
105 @app.route(_add_static_prefix("/ajax-api/2.0/mlflow/upload-artifact"), methods=["POST"])
106 def serve_upload_artifact():
107     return upload_artifact_handler()
108 
109 
110 # We expect the react app to be built assuming it is hosted at /static-files, so that requests for
111 # CSS/JS resources will be made to e.g. /static-files/main.css and we can handle them here.
112 # The files are hashed based on source code, so ok to send Cache-Control headers via max_age.
113 @app.route(_add_static_prefix("/static-files/<path:path>"))
114 def serve_static_file(path):
115     if IS_FLASK_V1:
116         return send_from_directory(app.static_folder, path, cache_timeout=2419200)
117     else:
118         return send_from_directory(app.static_folder, path, max_age=2419200)
119 
120 
121 # Serve the index.html for the React App for all other routes.
122 @app.route(_add_static_prefix("/"))
123 def serve():
124     if os.path.exists(os.path.join(app.static_folder, "index.html")):
125         return send_from_directory(app.static_folder, "index.html")
126 
127     text = textwrap.dedent(
128         """
129     Unable to display MLflow UI - landing page (index.html) not found.
130 
131     You are very likely running the MLflow server using a source installation of the Python MLflow
132     package.
133 
134     If you are a developer making MLflow source code changes and intentionally running a source
135     installation of MLflow, you can view the UI by running the Javascript dev server:
136     https://github.com/mlflow/mlflow/blob/master/CONTRIBUTING.md#running-the-javascript-dev-server
137 
138     Otherwise, uninstall MLflow via 'pip uninstall mlflow', reinstall an official MLflow release
139     from PyPI via 'pip install mlflow', and rerun the MLflow server.
140     """
141     )
142     return Response(text, mimetype="text/plain")
143 
144 
145 def _find_app(app_name: str) -> str:
146     apps = importlib.metadata.entry_points().get("mlflow.app", [])
147     for app in apps:
148         if app.name == app_name:
149             return app.value
150 
151     raise MlflowException(
152         f"Failed to find app '{app_name}'. Available apps: {[a.name for a in apps]}"
153     )
154 
155 
156 def _is_factory(app: str) -> bool:
157     """
158     Returns True if the given app is a factory function, False otherwise.
159 
160     :param app: The app to check, e.g. "mlflow.server.app:app"
161     """
162     module, obj_name = app.rsplit(":", 1)
163     mod = importlib.import_module(module)
164     obj = getattr(mod, obj_name)
165     return isinstance(obj, types.FunctionType)
166 
167 
168 def get_app_client(app_name: str, *args, **kwargs):
169     """
170     Instantiate a client provided by an app.
171 
172     :param app_name: The app name defined in `setup.py`, e.g., "basic-auth".
173     :param args: Additional arguments passed to the app client constructor.
174     :param kwargs: Additional keyword arguments passed to the app client constructor.
175     :return: An app client instance.
176     """
177     clients = importlib.metadata.entry_points().get("mlflow.app.client", [])
178     for client in clients:
179         if client.name == app_name:
180             cls = client.load()
181             return cls(*args, **kwargs)
182 
183     raise MlflowException(
184         f"Failed to find client for '{app_name}'. Available clients: {[c.name for c in clients]}"
185     )
186 
187 
188 def _build_waitress_command(waitress_opts, host, port, app_name, is_factory):
189     opts = shlex.split(waitress_opts) if waitress_opts else []
190     return [
191         sys.executable,
192         "-m",
193         "waitress",
194         *opts,
195         f"--host={host}",
196         f"--port={port}",
197         "--ident=mlflow",
198         *(["--call"] if is_factory else []),
199         app_name,
200     ]
201 
202 
203 def _build_gunicorn_command(gunicorn_opts, host, port, workers, app_name):
204     bind_address = f"{host}:{port}"
205     opts = shlex.split(gunicorn_opts) if gunicorn_opts else []
206     return [
207         sys.executable,
208         "-m",
209         "gunicorn",
210         *opts,
211         "-b",
212         bind_address,
213         "-w",
214         str(workers),
215         app_name,
216     ]
217 
218 
219 def _run_server(
220     file_store_path,
221     registry_store_uri,
222     default_artifact_root,
223     serve_artifacts,
224     artifacts_only,
225     artifacts_destination,
226     host,
227     port,
228     static_prefix=None,
229     workers=None,
230     gunicorn_opts=None,
231     waitress_opts=None,
232     expose_prometheus=None,
233     app_name=None,
234 ):
235     """
236     Run the MLflow server, wrapping it in gunicorn or waitress on windows
237     :param static_prefix: If set, the index.html asset will be served from the path static_prefix.
238                           If left None, the index.html asset will be served from the root path.
239     :return: None
240     """
241     env_map = {}
242     if file_store_path:
243         env_map[BACKEND_STORE_URI_ENV_VAR] = file_store_path
244     if registry_store_uri:
245         env_map[REGISTRY_STORE_URI_ENV_VAR] = registry_store_uri
246     if default_artifact_root:
247         env_map[ARTIFACT_ROOT_ENV_VAR] = default_artifact_root
248     if serve_artifacts:
249         env_map[SERVE_ARTIFACTS_ENV_VAR] = "true"
250     if artifacts_only:
251         env_map[ARTIFACTS_ONLY_ENV_VAR] = "true"
252     if artifacts_destination:
253         env_map[ARTIFACTS_DESTINATION_ENV_VAR] = artifacts_destination
254     if static_prefix:
255         env_map[STATIC_PREFIX_ENV_VAR] = static_prefix
256 
257     if expose_prometheus:
258         env_map[PROMETHEUS_EXPORTER_ENV_VAR] = expose_prometheus
259 
260     if app_name is None:
261         app = f"{__name__}:app"
262         is_factory = False
263     else:
264         app = _find_app(app_name)
265         is_factory = _is_factory(app)
266         # `waitress` doesn't support `()` syntax for factory functions.
267         # Instead, we need to use the `--call` flag.
268         app = f"{app}()" if (not is_windows() and is_factory) else app
269 
270     # TODO: eventually may want waitress on non-win32
271     if sys.platform == "win32":
272         full_command = _build_waitress_command(waitress_opts, host, port, app, is_factory)
273     else:
274         full_command = _build_gunicorn_command(gunicorn_opts, host, port, workers or 4, app)
275     _exec_cmd(full_command, extra_env=env_map, capture_output=False)
276
```
--- 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/mlflow/server/__init__.py b/mlflow/server/__init__.py
--- a/mlflow/server/__init__.py
+++ b/mlflow/server/__init__.py
@@ -7,7 +7,6 @@
 import types
 
 from flask import Flask, Response, send_from_directory
-from flask import __version__ as flask_version
 from packaging.version import Version
 
 from mlflow.exceptions import MlflowException
@@ -40,7 +39,7 @@
 REL_STATIC_DIR = "js/build"
 
 app = Flask(__name__, static_folder=REL_STATIC_DIR)
-IS_FLASK_V1 = Version(flask_version) < Version("2.0")
+IS_FLASK_V1 = Version(importlib.metadata.version("flask")) < Version("2.0")
 
 
 for http_path, handler, methods in handlers.get_endpoints():

{"golden_diff": "diff --git a/mlflow/server/__init__.py b/mlflow/server/__init__.py\n--- a/mlflow/server/__init__.py\n+++ b/mlflow/server/__init__.py\n@@ -7,7 +7,6 @@\n import types\n \n from flask import Flask, Response, send_from_directory\n-from flask import __version__ as flask_version\n from packaging.version import Version\n \n from mlflow.exceptions import MlflowException\n@@ -40,7 +39,7 @@\n REL_STATIC_DIR = \"js/build\"\n \n app = Flask(__name__, static_folder=REL_STATIC_DIR)\n-IS_FLASK_V1 = Version(flask_version) < Version(\"2.0\")\n+IS_FLASK_V1 = Version(importlib.metadata.version(\"flask\")) < Version(\"2.0\")\n \n \n for http_path, handler, methods in handlers.get_endpoints():\n", "issue": "Support Flask 3.0 in MLflow\n### Summary\r\n\r\nWe currently pin `Flask` to `< 3.0`. Flask 3.0 was recently released. We should update the requirement:\r\n\r\n```diff\r\ndiff --git a/requirements/core-requirements.txt b/requirements/core-requirements.txt\r\nindex 99f4cdd62..11f7e3770 100644\r\n--- a/requirements/core-requirements.txt\r\n+++ b/requirements/core-requirements.txt\r\n@@ -4,7 +4,7 @@\r\n \r\n alembic<2,!=1.10.0\r\n docker<7,>=4.0.0\r\n-Flask<3\r\n+Flask<4\r\n numpy<2\r\n scipy<2\r\n pandas<3\r\ndiff --git a/requirements/core-requirements.yaml b/requirements/core-requirements.yaml\r\nindex 5447b07c7..4ffb44c6b 100644\r\n--- a/requirements/core-requirements.yaml\r\n+++ b/requirements/core-requirements.yaml\r\n@@ -17,7 +17,7 @@ docker:\r\n \r\n flask:\r\n   pip_release: Flask\r\n-  max_major_version: 2\r\n+  max_major_version: 3\r\n \r\n numpy:\r\n   pip_release: numpy\r\n```\r\n\r\nReferences:\r\n\r\n- https://pypi.org/project/Flask/3.0.0/\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 `#{issue_number}` (e.g. `#123`) in the PR description when opening a PR.\r\n\n", "before_files": [{"content": "import importlib\nimport importlib.metadata\nimport os\nimport shlex\nimport sys\nimport textwrap\nimport types\n\nfrom flask import Flask, Response, send_from_directory\nfrom flask import __version__ as flask_version\nfrom packaging.version import Version\n\nfrom mlflow.exceptions import MlflowException\nfrom mlflow.server import handlers\nfrom mlflow.server.handlers import (\n    STATIC_PREFIX_ENV_VAR,\n    _add_static_prefix,\n    create_promptlab_run_handler,\n    gateway_proxy_handler,\n    get_artifact_handler,\n    get_metric_history_bulk_handler,\n    get_model_version_artifact_handler,\n    search_datasets_handler,\n    upload_artifact_handler,\n)\nfrom mlflow.utils.os import is_windows\nfrom mlflow.utils.process import _exec_cmd\nfrom mlflow.version import VERSION\n\n# NB: These are internal environment variables used for communication between\n# the cli and the forked gunicorn processes.\nBACKEND_STORE_URI_ENV_VAR = \"_MLFLOW_SERVER_FILE_STORE\"\nREGISTRY_STORE_URI_ENV_VAR = \"_MLFLOW_SERVER_REGISTRY_STORE\"\nARTIFACT_ROOT_ENV_VAR = \"_MLFLOW_SERVER_ARTIFACT_ROOT\"\nARTIFACTS_DESTINATION_ENV_VAR = \"_MLFLOW_SERVER_ARTIFACT_DESTINATION\"\nPROMETHEUS_EXPORTER_ENV_VAR = \"prometheus_multiproc_dir\"\nSERVE_ARTIFACTS_ENV_VAR = \"_MLFLOW_SERVER_SERVE_ARTIFACTS\"\nARTIFACTS_ONLY_ENV_VAR = \"_MLFLOW_SERVER_ARTIFACTS_ONLY\"\n\nREL_STATIC_DIR = \"js/build\"\n\napp = Flask(__name__, static_folder=REL_STATIC_DIR)\nIS_FLASK_V1 = Version(flask_version) < Version(\"2.0\")\n\n\nfor http_path, handler, methods in handlers.get_endpoints():\n    app.add_url_rule(http_path, handler.__name__, handler, methods=methods)\n\nif os.getenv(PROMETHEUS_EXPORTER_ENV_VAR):\n    from mlflow.server.prometheus_exporter import activate_prometheus_exporter\n\n    prometheus_metrics_path = os.getenv(PROMETHEUS_EXPORTER_ENV_VAR)\n    if not os.path.exists(prometheus_metrics_path):\n        os.makedirs(prometheus_metrics_path)\n    activate_prometheus_exporter(app)\n\n\n# Provide a health check endpoint to ensure the application is responsive\n@app.route(\"/health\")\ndef health():\n    return \"OK\", 200\n\n\n# Provide an endpoint to query the version of mlflow running on the server\n@app.route(\"/version\")\ndef version():\n    return VERSION, 200\n\n\n# Serve the \"get-artifact\" route.\n@app.route(_add_static_prefix(\"/get-artifact\"))\ndef serve_artifacts():\n    return get_artifact_handler()\n\n\n# Serve the \"model-versions/get-artifact\" route.\n@app.route(_add_static_prefix(\"/model-versions/get-artifact\"))\ndef serve_model_version_artifact():\n    return get_model_version_artifact_handler()\n\n\n# Serve the \"metrics/get-history-bulk\" route.\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/metrics/get-history-bulk\"))\ndef serve_get_metric_history_bulk():\n    return get_metric_history_bulk_handler()\n\n\n# Serve the \"experiments/search-datasets\" route.\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/experiments/search-datasets\"), methods=[\"POST\"])\ndef serve_search_datasets():\n    return search_datasets_handler()\n\n\n# Serve the \"runs/create-promptlab-run\" route.\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/runs/create-promptlab-run\"), methods=[\"POST\"])\ndef serve_create_promptlab_run():\n    return create_promptlab_run_handler()\n\n\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/gateway-proxy\"), methods=[\"POST\", \"GET\"])\ndef serve_gateway_proxy():\n    return gateway_proxy_handler()\n\n\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/upload-artifact\"), methods=[\"POST\"])\ndef serve_upload_artifact():\n    return upload_artifact_handler()\n\n\n# We expect the react app to be built assuming it is hosted at /static-files, so that requests for\n# CSS/JS resources will be made to e.g. /static-files/main.css and we can handle them here.\n# The files are hashed based on source code, so ok to send Cache-Control headers via max_age.\n@app.route(_add_static_prefix(\"/static-files/<path:path>\"))\ndef serve_static_file(path):\n    if IS_FLASK_V1:\n        return send_from_directory(app.static_folder, path, cache_timeout=2419200)\n    else:\n        return send_from_directory(app.static_folder, path, max_age=2419200)\n\n\n# Serve the index.html for the React App for all other routes.\n@app.route(_add_static_prefix(\"/\"))\ndef serve():\n    if os.path.exists(os.path.join(app.static_folder, \"index.html\")):\n        return send_from_directory(app.static_folder, \"index.html\")\n\n    text = textwrap.dedent(\n        \"\"\"\n    Unable to display MLflow UI - landing page (index.html) not found.\n\n    You are very likely running the MLflow server using a source installation of the Python MLflow\n    package.\n\n    If you are a developer making MLflow source code changes and intentionally running a source\n    installation of MLflow, you can view the UI by running the Javascript dev server:\n    https://github.com/mlflow/mlflow/blob/master/CONTRIBUTING.md#running-the-javascript-dev-server\n\n    Otherwise, uninstall MLflow via 'pip uninstall mlflow', reinstall an official MLflow release\n    from PyPI via 'pip install mlflow', and rerun the MLflow server.\n    \"\"\"\n    )\n    return Response(text, mimetype=\"text/plain\")\n\n\ndef _find_app(app_name: str) -> str:\n    apps = importlib.metadata.entry_points().get(\"mlflow.app\", [])\n    for app in apps:\n        if app.name == app_name:\n            return app.value\n\n    raise MlflowException(\n        f\"Failed to find app '{app_name}'. Available apps: {[a.name for a in apps]}\"\n    )\n\n\ndef _is_factory(app: str) -> bool:\n    \"\"\"\n    Returns True if the given app is a factory function, False otherwise.\n\n    :param app: The app to check, e.g. \"mlflow.server.app:app\"\n    \"\"\"\n    module, obj_name = app.rsplit(\":\", 1)\n    mod = importlib.import_module(module)\n    obj = getattr(mod, obj_name)\n    return isinstance(obj, types.FunctionType)\n\n\ndef get_app_client(app_name: str, *args, **kwargs):\n    \"\"\"\n    Instantiate a client provided by an app.\n\n    :param app_name: The app name defined in `setup.py`, e.g., \"basic-auth\".\n    :param args: Additional arguments passed to the app client constructor.\n    :param kwargs: Additional keyword arguments passed to the app client constructor.\n    :return: An app client instance.\n    \"\"\"\n    clients = importlib.metadata.entry_points().get(\"mlflow.app.client\", [])\n    for client in clients:\n        if client.name == app_name:\n            cls = client.load()\n            return cls(*args, **kwargs)\n\n    raise MlflowException(\n        f\"Failed to find client for '{app_name}'. Available clients: {[c.name for c in clients]}\"\n    )\n\n\ndef _build_waitress_command(waitress_opts, host, port, app_name, is_factory):\n    opts = shlex.split(waitress_opts) if waitress_opts else []\n    return [\n        sys.executable,\n        \"-m\",\n        \"waitress\",\n        *opts,\n        f\"--host={host}\",\n        f\"--port={port}\",\n        \"--ident=mlflow\",\n        *([\"--call\"] if is_factory else []),\n        app_name,\n    ]\n\n\ndef _build_gunicorn_command(gunicorn_opts, host, port, workers, app_name):\n    bind_address = f\"{host}:{port}\"\n    opts = shlex.split(gunicorn_opts) if gunicorn_opts else []\n    return [\n        sys.executable,\n        \"-m\",\n        \"gunicorn\",\n        *opts,\n        \"-b\",\n        bind_address,\n        \"-w\",\n        str(workers),\n        app_name,\n    ]\n\n\ndef _run_server(\n    file_store_path,\n    registry_store_uri,\n    default_artifact_root,\n    serve_artifacts,\n    artifacts_only,\n    artifacts_destination,\n    host,\n    port,\n    static_prefix=None,\n    workers=None,\n    gunicorn_opts=None,\n    waitress_opts=None,\n    expose_prometheus=None,\n    app_name=None,\n):\n    \"\"\"\n    Run the MLflow server, wrapping it in gunicorn or waitress on windows\n    :param static_prefix: If set, the index.html asset will be served from the path static_prefix.\n                          If left None, the index.html asset will be served from the root path.\n    :return: None\n    \"\"\"\n    env_map = {}\n    if file_store_path:\n        env_map[BACKEND_STORE_URI_ENV_VAR] = file_store_path\n    if registry_store_uri:\n        env_map[REGISTRY_STORE_URI_ENV_VAR] = registry_store_uri\n    if default_artifact_root:\n        env_map[ARTIFACT_ROOT_ENV_VAR] = default_artifact_root\n    if serve_artifacts:\n        env_map[SERVE_ARTIFACTS_ENV_VAR] = \"true\"\n    if artifacts_only:\n        env_map[ARTIFACTS_ONLY_ENV_VAR] = \"true\"\n    if artifacts_destination:\n        env_map[ARTIFACTS_DESTINATION_ENV_VAR] = artifacts_destination\n    if static_prefix:\n        env_map[STATIC_PREFIX_ENV_VAR] = static_prefix\n\n    if expose_prometheus:\n        env_map[PROMETHEUS_EXPORTER_ENV_VAR] = expose_prometheus\n\n    if app_name is None:\n        app = f\"{__name__}:app\"\n        is_factory = False\n    else:\n        app = _find_app(app_name)\n        is_factory = _is_factory(app)\n        # `waitress` doesn't support `()` syntax for factory functions.\n        # Instead, we need to use the `--call` flag.\n        app = f\"{app}()\" if (not is_windows() and is_factory) else app\n\n    # TODO: eventually may want waitress on non-win32\n    if sys.platform == \"win32\":\n        full_command = _build_waitress_command(waitress_opts, host, port, app, is_factory)\n    else:\n        full_command = _build_gunicorn_command(gunicorn_opts, host, port, workers or 4, app)\n    _exec_cmd(full_command, extra_env=env_map, capture_output=False)\n", "path": "mlflow/server/__init__.py"}], "after_files": [{"content": "import importlib\nimport importlib.metadata\nimport os\nimport shlex\nimport sys\nimport textwrap\nimport types\n\nfrom flask import Flask, Response, send_from_directory\nfrom packaging.version import Version\n\nfrom mlflow.exceptions import MlflowException\nfrom mlflow.server import handlers\nfrom mlflow.server.handlers import (\n    STATIC_PREFIX_ENV_VAR,\n    _add_static_prefix,\n    create_promptlab_run_handler,\n    gateway_proxy_handler,\n    get_artifact_handler,\n    get_metric_history_bulk_handler,\n    get_model_version_artifact_handler,\n    search_datasets_handler,\n    upload_artifact_handler,\n)\nfrom mlflow.utils.os import is_windows\nfrom mlflow.utils.process import _exec_cmd\nfrom mlflow.version import VERSION\n\n# NB: These are internal environment variables used for communication between\n# the cli and the forked gunicorn processes.\nBACKEND_STORE_URI_ENV_VAR = \"_MLFLOW_SERVER_FILE_STORE\"\nREGISTRY_STORE_URI_ENV_VAR = \"_MLFLOW_SERVER_REGISTRY_STORE\"\nARTIFACT_ROOT_ENV_VAR = \"_MLFLOW_SERVER_ARTIFACT_ROOT\"\nARTIFACTS_DESTINATION_ENV_VAR = \"_MLFLOW_SERVER_ARTIFACT_DESTINATION\"\nPROMETHEUS_EXPORTER_ENV_VAR = \"prometheus_multiproc_dir\"\nSERVE_ARTIFACTS_ENV_VAR = \"_MLFLOW_SERVER_SERVE_ARTIFACTS\"\nARTIFACTS_ONLY_ENV_VAR = \"_MLFLOW_SERVER_ARTIFACTS_ONLY\"\n\nREL_STATIC_DIR = \"js/build\"\n\napp = Flask(__name__, static_folder=REL_STATIC_DIR)\nIS_FLASK_V1 = Version(importlib.metadata.version(\"flask\")) < Version(\"2.0\")\n\n\nfor http_path, handler, methods in handlers.get_endpoints():\n    app.add_url_rule(http_path, handler.__name__, handler, methods=methods)\n\nif os.getenv(PROMETHEUS_EXPORTER_ENV_VAR):\n    from mlflow.server.prometheus_exporter import activate_prometheus_exporter\n\n    prometheus_metrics_path = os.getenv(PROMETHEUS_EXPORTER_ENV_VAR)\n    if not os.path.exists(prometheus_metrics_path):\n        os.makedirs(prometheus_metrics_path)\n    activate_prometheus_exporter(app)\n\n\n# Provide a health check endpoint to ensure the application is responsive\n@app.route(\"/health\")\ndef health():\n    return \"OK\", 200\n\n\n# Provide an endpoint to query the version of mlflow running on the server\n@app.route(\"/version\")\ndef version():\n    return VERSION, 200\n\n\n# Serve the \"get-artifact\" route.\n@app.route(_add_static_prefix(\"/get-artifact\"))\ndef serve_artifacts():\n    return get_artifact_handler()\n\n\n# Serve the \"model-versions/get-artifact\" route.\n@app.route(_add_static_prefix(\"/model-versions/get-artifact\"))\ndef serve_model_version_artifact():\n    return get_model_version_artifact_handler()\n\n\n# Serve the \"metrics/get-history-bulk\" route.\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/metrics/get-history-bulk\"))\ndef serve_get_metric_history_bulk():\n    return get_metric_history_bulk_handler()\n\n\n# Serve the \"experiments/search-datasets\" route.\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/experiments/search-datasets\"), methods=[\"POST\"])\ndef serve_search_datasets():\n    return search_datasets_handler()\n\n\n# Serve the \"runs/create-promptlab-run\" route.\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/runs/create-promptlab-run\"), methods=[\"POST\"])\ndef serve_create_promptlab_run():\n    return create_promptlab_run_handler()\n\n\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/gateway-proxy\"), methods=[\"POST\", \"GET\"])\ndef serve_gateway_proxy():\n    return gateway_proxy_handler()\n\n\n@app.route(_add_static_prefix(\"/ajax-api/2.0/mlflow/upload-artifact\"), methods=[\"POST\"])\ndef serve_upload_artifact():\n    return upload_artifact_handler()\n\n\n# We expect the react app to be built assuming it is hosted at /static-files, so that requests for\n# CSS/JS resources will be made to e.g. /static-files/main.css and we can handle them here.\n# The files are hashed based on source code, so ok to send Cache-Control headers via max_age.\n@app.route(_add_static_prefix(\"/static-files/<path:path>\"))\ndef serve_static_file(path):\n    if IS_FLASK_V1:\n        return send_from_directory(app.static_folder, path, cache_timeout=2419200)\n    else:\n        return send_from_directory(app.static_folder, path, max_age=2419200)\n\n\n# Serve the index.html for the React App for all other routes.\n@app.route(_add_static_prefix(\"/\"))\ndef serve():\n    if os.path.exists(os.path.join(app.static_folder, \"index.html\")):\n        return send_from_directory(app.static_folder, \"index.html\")\n\n    text = textwrap.dedent(\n        \"\"\"\n    Unable to display MLflow UI - landing page (index.html) not found.\n\n    You are very likely running the MLflow server using a source installation of the Python MLflow\n    package.\n\n    If you are a developer making MLflow source code changes and intentionally running a source\n    installation of MLflow, you can view the UI by running the Javascript dev server:\n    https://github.com/mlflow/mlflow/blob/master/CONTRIBUTING.md#running-the-javascript-dev-server\n\n    Otherwise, uninstall MLflow via 'pip uninstall mlflow', reinstall an official MLflow release\n    from PyPI via 'pip install mlflow', and rerun the MLflow server.\n    \"\"\"\n    )\n    return Response(text, mimetype=\"text/plain\")\n\n\ndef _find_app(app_name: str) -> str:\n    apps = importlib.metadata.entry_points().get(\"mlflow.app\", [])\n    for app in apps:\n        if app.name == app_name:\n            return app.value\n\n    raise MlflowException(\n        f\"Failed to find app '{app_name}'. Available apps: {[a.name for a in apps]}\"\n    )\n\n\ndef _is_factory(app: str) -> bool:\n    \"\"\"\n    Returns True if the given app is a factory function, False otherwise.\n\n    :param app: The app to check, e.g. \"mlflow.server.app:app\"\n    \"\"\"\n    module, obj_name = app.rsplit(\":\", 1)\n    mod = importlib.import_module(module)\n    obj = getattr(mod, obj_name)\n    return isinstance(obj, types.FunctionType)\n\n\ndef get_app_client(app_name: str, *args, **kwargs):\n    \"\"\"\n    Instantiate a client provided by an app.\n\n    :param app_name: The app name defined in `setup.py`, e.g., \"basic-auth\".\n    :param args: Additional arguments passed to the app client constructor.\n    :param kwargs: Additional keyword arguments passed to the app client constructor.\n    :return: An app client instance.\n    \"\"\"\n    clients = importlib.metadata.entry_points().get(\"mlflow.app.client\", [])\n    for client in clients:\n        if client.name == app_name:\n            cls = client.load()\n            return cls(*args, **kwargs)\n\n    raise MlflowException(\n        f\"Failed to find client for '{app_name}'. Available clients: {[c.name for c in clients]}\"\n    )\n\n\ndef _build_waitress_command(waitress_opts, host, port, app_name, is_factory):\n    opts = shlex.split(waitress_opts) if waitress_opts else []\n    return [\n        sys.executable,\n        \"-m\",\n        \"waitress\",\n        *opts,\n        f\"--host={host}\",\n        f\"--port={port}\",\n        \"--ident=mlflow\",\n        *([\"--call\"] if is_factory else []),\n        app_name,\n    ]\n\n\ndef _build_gunicorn_command(gunicorn_opts, host, port, workers, app_name):\n    bind_address = f\"{host}:{port}\"\n    opts = shlex.split(gunicorn_opts) if gunicorn_opts else []\n    return [\n        sys.executable,\n        \"-m\",\n        \"gunicorn\",\n        *opts,\n        \"-b\",\n        bind_address,\n        \"-w\",\n        str(workers),\n        app_name,\n    ]\n\n\ndef _run_server(\n    file_store_path,\n    registry_store_uri,\n    default_artifact_root,\n    serve_artifacts,\n    artifacts_only,\n    artifacts_destination,\n    host,\n    port,\n    static_prefix=None,\n    workers=None,\n    gunicorn_opts=None,\n    waitress_opts=None,\n    expose_prometheus=None,\n    app_name=None,\n):\n    \"\"\"\n    Run the MLflow server, wrapping it in gunicorn or waitress on windows\n    :param static_prefix: If set, the index.html asset will be served from the path static_prefix.\n                          If left None, the index.html asset will be served from the root path.\n    :return: None\n    \"\"\"\n    env_map = {}\n    if file_store_path:\n        env_map[BACKEND_STORE_URI_ENV_VAR] = file_store_path\n    if registry_store_uri:\n        env_map[REGISTRY_STORE_URI_ENV_VAR] = registry_store_uri\n    if default_artifact_root:\n        env_map[ARTIFACT_ROOT_ENV_VAR] = default_artifact_root\n    if serve_artifacts:\n        env_map[SERVE_ARTIFACTS_ENV_VAR] = \"true\"\n    if artifacts_only:\n        env_map[ARTIFACTS_ONLY_ENV_VAR] = \"true\"\n    if artifacts_destination:\n        env_map[ARTIFACTS_DESTINATION_ENV_VAR] = artifacts_destination\n    if static_prefix:\n        env_map[STATIC_PREFIX_ENV_VAR] = static_prefix\n\n    if expose_prometheus:\n        env_map[PROMETHEUS_EXPORTER_ENV_VAR] = expose_prometheus\n\n    if app_name is None:\n        app = f\"{__name__}:app\"\n        is_factory = False\n    else:\n        app = _find_app(app_name)\n        is_factory = _is_factory(app)\n        # `waitress` doesn't support `()` syntax for factory functions.\n        # Instead, we need to use the `--call` flag.\n        app = f\"{app}()\" if (not is_windows() and is_factory) else app\n\n    # TODO: eventually may want waitress on non-win32\n    if sys.platform == \"win32\":\n        full_command = _build_waitress_command(waitress_opts, host, port, app, is_factory)\n    else:\n        full_command = _build_gunicorn_command(gunicorn_opts, host, port, workers or 4, app)\n    _exec_cmd(full_command, extra_env=env_map, capture_output=False)\n", "path": "mlflow/server/__init__.py"}]}