matlab-proxy 0.25.1__py3-none-any.whl → 0.27.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of matlab-proxy might be problematic. Click here for more details.

Files changed (63) hide show
  1. matlab_proxy/app.py +68 -16
  2. matlab_proxy/app_state.py +8 -2
  3. matlab_proxy/constants.py +1 -0
  4. matlab_proxy/default_configuration.py +2 -2
  5. matlab_proxy/gui/index.html +1 -1
  6. matlab_proxy/gui/static/js/{index.CZgGkMCD.js → index.BcDShXfH.js} +16 -16
  7. matlab_proxy/settings.py +24 -2
  8. matlab_proxy/util/cookie_jar.py +72 -0
  9. matlab_proxy/util/list_servers.py +2 -2
  10. matlab_proxy/util/mwi/environment_variables.py +15 -0
  11. matlab_proxy/util/mwi/session_name.py +28 -0
  12. matlab_proxy/util/mwi/validators.py +2 -4
  13. {matlab_proxy-0.25.1.dist-info → matlab_proxy-0.27.0.dist-info}/METADATA +37 -23
  14. {matlab_proxy-0.25.1.dist-info → matlab_proxy-0.27.0.dist-info}/RECORD +29 -56
  15. {matlab_proxy-0.25.1.dist-info → matlab_proxy-0.27.0.dist-info}/WHEEL +1 -2
  16. matlab_proxy_manager/README.md +85 -0
  17. matlab_proxy_manager/lib/README.md +53 -0
  18. matlab_proxy_manager/lib/api.py +156 -114
  19. matlab_proxy_manager/storage/README.md +54 -0
  20. matlab_proxy_manager/storage/server.py +5 -2
  21. matlab_proxy_manager/utils/constants.py +2 -1
  22. matlab_proxy_manager/utils/environment_variables.py +6 -1
  23. matlab_proxy_manager/utils/exceptions.py +45 -0
  24. matlab_proxy_manager/utils/helpers.py +2 -2
  25. matlab_proxy_manager/utils/logger.py +4 -1
  26. matlab_proxy_manager/web/README.md +37 -0
  27. matlab_proxy_manager/web/app.py +71 -19
  28. matlab_proxy-0.25.1.dist-info/top_level.txt +0 -3
  29. tests/integration/__init__.py +0 -1
  30. tests/integration/integration_tests_with_license/__init__.py +0 -1
  31. tests/integration/integration_tests_with_license/conftest.py +0 -47
  32. tests/integration/integration_tests_with_license/test_http_end_points.py +0 -397
  33. tests/integration/integration_tests_without_license/__init__.py +0 -1
  34. tests/integration/integration_tests_without_license/conftest.py +0 -116
  35. tests/integration/integration_tests_without_license/test_matlab_is_down_if_unlicensed.py +0 -49
  36. tests/integration/utils/__init__.py +0 -1
  37. tests/integration/utils/integration_tests_utils.py +0 -352
  38. tests/integration/utils/licensing.py +0 -152
  39. tests/unit/__init__.py +0 -1
  40. tests/unit/conftest.py +0 -66
  41. tests/unit/test_app.py +0 -1200
  42. tests/unit/test_app_state.py +0 -1094
  43. tests/unit/test_constants.py +0 -7
  44. tests/unit/test_ddux.py +0 -22
  45. tests/unit/test_devel.py +0 -246
  46. tests/unit/test_non_dev_mode.py +0 -169
  47. tests/unit/test_settings.py +0 -659
  48. tests/unit/util/__init__.py +0 -3
  49. tests/unit/util/mwi/__init__.py +0 -1
  50. tests/unit/util/mwi/embedded_connector/__init__.py +0 -1
  51. tests/unit/util/mwi/embedded_connector/test_helpers.py +0 -29
  52. tests/unit/util/mwi/embedded_connector/test_request.py +0 -64
  53. tests/unit/util/mwi/test_custom_http_headers.py +0 -281
  54. tests/unit/util/mwi/test_download.py +0 -152
  55. tests/unit/util/mwi/test_logger.py +0 -82
  56. tests/unit/util/mwi/test_token_auth.py +0 -303
  57. tests/unit/util/mwi/test_validators.py +0 -364
  58. tests/unit/util/test_mw.py +0 -550
  59. tests/unit/util/test_util.py +0 -221
  60. tests/utils/__init__.py +0 -1
  61. tests/utils/logging_util.py +0 -81
  62. {matlab_proxy-0.25.1.dist-info → matlab_proxy-0.27.0.dist-info}/entry_points.txt +0 -0
  63. {matlab_proxy-0.25.1.dist-info → matlab_proxy-0.27.0.dist-info/licenses}/LICENSE.md +0 -0
@@ -3,13 +3,14 @@ import asyncio
3
3
  import os
4
4
  import secrets
5
5
  import subprocess
6
- from typing import List, Optional, Tuple
6
+ from typing import List, Tuple
7
7
 
8
8
  import matlab_proxy
9
+ import matlab_proxy.util.mwi.environment_variables as mwi_env
9
10
  import matlab_proxy.util.system as mwi_sys
10
11
  from matlab_proxy_manager.storage.file_repository import FileRepository
11
12
  from matlab_proxy_manager.storage.server import ServerProcess
12
- from matlab_proxy_manager.utils import constants, helpers, logger
13
+ from matlab_proxy_manager.utils import constants, exceptions, helpers, logger
13
14
 
14
15
  # Used to list all the public-facing APIs exported by this module.
15
16
  __all__ = ["shutdown", "start_matlab_proxy_for_kernel", "start_matlab_proxy_for_jsp"]
@@ -20,7 +21,7 @@ log = logger.get(init=True)
20
21
 
21
22
 
22
23
  async def start_matlab_proxy_for_kernel(
23
- caller_id: str, parent_id: str, is_shared_matlab: bool
24
+ caller_id: str, parent_id: str, is_shared_matlab: bool, base_url_prefix: str = ""
24
25
  ):
25
26
  """
26
27
  Starts a MATLAB proxy server specifically for MATLAB Kernel.
@@ -29,12 +30,18 @@ async def start_matlab_proxy_for_kernel(
29
30
  set to None, for starting the MATLAB proxy server via proxy manager.
30
31
  """
31
32
  return await _start_matlab_proxy(
32
- caller_id=caller_id, ctx=parent_id, is_shared_matlab=is_shared_matlab
33
+ caller_id=caller_id,
34
+ ctx=parent_id,
35
+ is_shared_matlab=is_shared_matlab,
36
+ base_url_prefix=base_url_prefix,
33
37
  )
34
38
 
35
39
 
36
40
  async def start_matlab_proxy_for_jsp(
37
- parent_id: str, is_shared_matlab: bool, mpm_auth_token: str
41
+ parent_id: str,
42
+ is_shared_matlab: bool,
43
+ mpm_auth_token: str,
44
+ base_url_prefix: str = "",
38
45
  ):
39
46
  """
40
47
  Starts a MATLAB proxy server specifically for Jupyter Server Proxy (JSP) - Open MATLAB launcher.
@@ -47,120 +54,136 @@ async def start_matlab_proxy_for_jsp(
47
54
  ctx=parent_id,
48
55
  is_shared_matlab=is_shared_matlab,
49
56
  mpm_auth_token=mpm_auth_token,
57
+ base_url_prefix=base_url_prefix,
50
58
  )
51
59
 
52
60
 
53
- async def _start_matlab_proxy(**options) -> Optional[dict]:
61
+ async def _start_matlab_proxy(**options) -> dict:
54
62
  """
55
- Start a MATLAB proxy server.
63
+ Starts a MATLAB proxy server with the specified options.
56
64
 
57
- This function starts a MATLAB proxy server based on the provided context and caller ID.
58
- It handles the creation of new servers and the reuse of existing ones.
65
+ This function validates the provided options, checks for existing server instances,
66
+ and either returns an existing server process or starts a new MATLAB proxy server.
67
+ It ensures that required arguments are present, handles token generation, and manages
68
+ server readiness and error handling.
59
69
 
60
- Args (keyword arguments):
61
- - caller_id (str): The identifier for the caller (kernel id for kernels, "jsp" for JSP).
62
- - ctx (str): The context in which the server is being started (parent pid).
63
- - is_shared_matlab (bool, optional): Whether to start a shared MATLAB proxy instance.
64
- Defaults to False.
65
- - mpm_auth_token (str, optional): The MATLAB proxy manager token. If not provided,
66
- a new token is generated. Defaults to None.
70
+ Args:
71
+ **options: Arbitrary keyword arguments containing the following keys:
72
+ - caller_id (str): The identifier for the caller (kernel id for kernels, "jsp" for JSP).
73
+ - ctx (str): The context in which the server is being started (parent pid).
74
+ - is_shared_matlab (bool): Flag indicating if the MATLAB proxy is shared.
75
+ - mpm_auth_token (Optional[str]): Authentication token for the MATLAB proxy manager.
76
+ - base_url_prefix (Optional[str]): Custom URL path which gets added to mwi_base_url
67
77
 
68
78
  Returns:
69
- ServerProcess: The process representing the MATLAB proxy server.
79
+ dict: A dictionary representation of the server process, including any errors encountered.
70
80
 
71
81
  Raises:
72
82
  ValueError: If `caller_id` is "default" and `is_shared_matlab` is False.
73
83
  """
74
- # Validate arguments
75
- required_args: List[str] = ["caller_id", "ctx", "is_shared_matlab"]
76
- missing_args: List[str] = [arg for arg in required_args if arg not in options]
77
-
78
- if missing_args:
79
- raise ValueError(f"Missing required arguments: {', '.join(missing_args)}")
84
+ _validate_required_arguments(options)
80
85
 
81
86
  caller_id: str = options["caller_id"]
82
87
  ctx: str = options["ctx"]
83
88
  is_shared_matlab: bool = options.get("is_shared_matlab", True)
84
- mpm_auth_token: Optional[str] = options.get("mpm_auth_token", None)
89
+ mpm_auth_token = options.get("mpm_auth_token", None) or secrets.token_hex(32)
85
90
 
86
91
  if not is_shared_matlab and caller_id == "default":
87
92
  raise ValueError(
88
93
  "Caller id cannot be default when matlab proxy is not shareable"
89
94
  )
90
95
 
91
- mpm_auth_token = mpm_auth_token or secrets.token_hex(32)
92
-
93
96
  # Cleanup stale entries before starting new instance of matlab proxy server
94
97
  helpers._are_orphaned_servers_deleted(ctx)
95
98
 
96
- ident = caller_id if not is_shared_matlab else "default"
97
- key = f"{ctx}_{ident}"
98
- log.debug("Starting matlab proxy using %s, %s, %s", ctx, ident, is_shared_matlab)
99
-
100
- data_dir = helpers.create_and_get_proxy_manager_data_dir()
101
- server_process = ServerProcess.find_existing_server(data_dir, key)
99
+ client_id = caller_id if not is_shared_matlab else "default"
100
+ matlab_session_dir = f"{ctx}_{client_id}"
101
+ filename = f"{ctx}_{caller_id}"
102
+ proxy_manager_root_dir = helpers.create_and_get_proxy_manager_data_dir()
103
+ existing_matlab_proxy_process = ServerProcess.find_existing_server(
104
+ proxy_manager_root_dir, matlab_session_dir
105
+ )
102
106
 
103
- if server_process:
107
+ if existing_matlab_proxy_process:
104
108
  log.debug("Found existing server for aliasing")
105
109
 
106
110
  # Create a backend file for this caller for reference tracking
107
- helpers.create_state_file(data_dir, server_process, f"{ctx}_{caller_id}")
111
+ helpers.create_state_file(
112
+ proxy_manager_root_dir, existing_matlab_proxy_process, filename
113
+ )
114
+
115
+ return existing_matlab_proxy_process.as_dict()
108
116
 
109
117
  # Create a new matlab proxy server
110
- else:
111
- server_process = await _start_subprocess_and_check_for_readiness(
112
- ident, ctx, key, is_shared_matlab, mpm_auth_token
118
+ try:
119
+ base_url_prefix = options.get("base_url_prefix", "")
120
+
121
+ # Prepare matlab proxy command and required environment variables
122
+ matlab_proxy_cmd, matlab_proxy_env = _prepare_cmd_and_env_for_matlab_proxy(
123
+ client_id, base_url_prefix
113
124
  )
114
125
 
126
+ log.debug(
127
+ "Starting new matlab proxy server using ctx=%s, client_id=%s, is_shared_matlab=%s",
128
+ ctx,
129
+ client_id,
130
+ is_shared_matlab,
131
+ )
132
+ # Start the matlab proxy process
133
+ process_id, url = await _start_subprocess(matlab_proxy_cmd, matlab_proxy_env)
134
+ log.debug("MATLAB proxy process url: %s", url)
135
+
136
+ matlab_proxy_process = ServerProcess(
137
+ server_url=url,
138
+ mwi_base_url=matlab_proxy_env.get(mwi_env.get_env_name_base_url()),
139
+ headers=helpers.convert_mwi_env_vars_to_header_format(
140
+ matlab_proxy_env, "MWI"
141
+ ),
142
+ pid=str(process_id),
143
+ parent_pid=ctx,
144
+ id=matlab_session_dir,
145
+ type="shared" if is_shared_matlab else "isolated",
146
+ mpm_auth_token=mpm_auth_token,
147
+ )
148
+
149
+ await _check_for_process_readiness(matlab_proxy_process)
150
+
115
151
  # Store the newly created server into filesystem
116
- if server_process:
117
- helpers.create_state_file(data_dir, server_process, f"{ctx}_{caller_id}")
152
+ helpers.create_state_file(
153
+ proxy_manager_root_dir, matlab_proxy_process, filename
154
+ )
155
+ return matlab_proxy_process.as_dict()
118
156
 
119
- return server_process.as_dict() if server_process else None
157
+ # Return a server process instance with the errors information set
158
+ except exceptions.ProcessStartError as pse:
159
+ return ServerProcess(errors=[str(pse)]).as_dict()
160
+ except exceptions.ServerReadinessError as sre:
161
+ return ServerProcess(errors=[str(sre)]).as_dict()
162
+ except Exception as e:
163
+ log.error("Error starting matlab proxy server: %s", str(e))
164
+ return ServerProcess(errors=[str(e)]).as_dict()
120
165
 
121
166
 
122
- async def _start_subprocess_and_check_for_readiness(
123
- server_id: str, ctx: str, key: str, is_shared_matlab: bool, mpm_auth_token: str
124
- ) -> Optional[ServerProcess]:
125
- """
126
- Starts a MATLAB proxy server.
167
+ def _validate_required_arguments(options):
168
+ # Validates that all required arguments are present in the supplied values
169
+ required_args: List[str] = ["caller_id", "ctx", "is_shared_matlab"]
170
+ missing_args: List[str] = [arg for arg in required_args if arg not in options]
127
171
 
128
- This function performs the following steps:
129
- 1. Prepares the command and environment variables required to start the MATLAB proxy server.
130
- 2. Initializes the MATLAB proxy process.
131
- 3. Checks if the MATLAB proxy server is ready.
132
- 4. Creates and returns a ServerProcess instance if the server is ready.
172
+ if missing_args:
173
+ raise ValueError(f"Missing required arguments: {', '.join(missing_args)}")
133
174
 
134
- Returns:
135
- Optional[ServerProcess]: An instance of ServerProcess if the server is successfully started,
136
- otherwise None.
175
+
176
+ async def _check_for_process_readiness(matlab_proxy_process: ServerProcess):
137
177
  """
138
- log.debug("Starting new matlab proxy server")
139
-
140
- # Prepare matlab proxy command and required environment variables
141
- matlab_proxy_cmd, matlab_proxy_env = _prepare_cmd_and_env_for_matlab_proxy()
142
-
143
- # Start the matlab proxy process
144
- result = await _start_subprocess(matlab_proxy_cmd, matlab_proxy_env, server_id)
145
- if not result:
146
- log.error("Could not start matlab proxy")
147
- return None
148
-
149
- process_id, url, mwi_base_url = result
150
-
151
- log.debug("Matlab proxy process info: %s, %s", url, mwi_base_url)
152
- matlab_proxy_process = ServerProcess(
153
- server_url=url,
154
- mwi_base_url=mwi_base_url,
155
- headers=helpers.convert_mwi_env_vars_to_header_format(matlab_proxy_env, "MWI"),
156
- pid=str(process_id),
157
- parent_pid=ctx,
158
- id=key,
159
- type="shared" if is_shared_matlab else "named",
160
- mpm_auth_token=mpm_auth_token,
161
- )
178
+ Checks if the MATLAB proxy server is ready.
162
179
 
163
- # Check for the matlab proxy server readiness
180
+ Args:
181
+ matlab_proxy_process (ServerProcess): Deserialized matlab-proxy process
182
+
183
+ Raises:
184
+ ServerReadinessError: If the MATLAB proxy server is not ready after retries.
185
+ """
186
+ # Check for the matlab proxy server readiness - with retries
164
187
  if not helpers.is_server_ready(
165
188
  url=matlab_proxy_process.absolute_url, retries=7, backoff_factor=0.5
166
189
  ):
@@ -168,19 +191,21 @@ async def _start_subprocess_and_check_for_readiness(
168
191
  "MATLAB Proxy Server unavailable: matlab-proxy-app failed to start or has timed out."
169
192
  )
170
193
  matlab_proxy_process.shutdown()
171
- matlab_proxy_process = None
172
-
173
- return matlab_proxy_process
194
+ raise exceptions.ServerReadinessError()
174
195
 
175
196
 
176
- def _prepare_cmd_and_env_for_matlab_proxy():
197
+ def _prepare_cmd_and_env_for_matlab_proxy(client_id: str, base_url_prefix: str):
177
198
  """
178
199
  Prepare the command and environment variables for starting the MATLAB proxy.
179
200
 
180
201
  Returns:
181
202
  Tuple: A tuple containing the MATLAB proxy command and environment variables.
182
203
  """
183
- from jupyter_matlab_proxy import config
204
+ # Get config from matlab_proxy module if jupyter_matlab_proxy module is not available
205
+ try:
206
+ from jupyter_matlab_proxy import config
207
+ except ImportError:
208
+ from matlab_proxy.default_configuration import config
184
209
 
185
210
  # Get the command to start matlab-proxy
186
211
  matlab_proxy_cmd: list = [
@@ -189,8 +214,12 @@ def _prepare_cmd_and_env_for_matlab_proxy():
189
214
  config.get("extension_name"),
190
215
  ]
191
216
 
217
+ mwi_base_url = _construct_mwi_base_url(base_url_prefix, client_id)
218
+ log.info("MWI_BASE_URL : %s", mwi_base_url)
219
+
192
220
  input_env: dict = {
193
221
  "MWI_AUTH_TOKEN": secrets.token_urlsafe(32),
222
+ "MWI_BASE_URL": mwi_base_url,
194
223
  }
195
224
 
196
225
  matlab_proxy_env: dict = os.environ.copy()
@@ -199,37 +228,55 @@ def _prepare_cmd_and_env_for_matlab_proxy():
199
228
  return matlab_proxy_cmd, matlab_proxy_env
200
229
 
201
230
 
202
- async def _start_subprocess(cmd, env, server_id) -> Optional[Tuple[int, str, str]]:
231
+ def _construct_mwi_base_url(base_url_prefix: str, client_id: str):
232
+ # Converts to correct base url (e.g. /jupyter/, default to /jupyter/matlab/default)
233
+ log.debug(
234
+ "base_url_prefix_from_client: %s, client_id: %s", base_url_prefix, client_id
235
+ )
236
+
237
+ if base_url_prefix:
238
+ base_url_prefix = base_url_prefix.rstrip("/")
239
+ prefix = constants.MWI_BASE_URL_PREFIX.strip("/")
240
+ client_id = client_id.strip("/")
241
+ return "/".join([base_url_prefix, prefix, client_id])
242
+
243
+
244
+ async def _start_subprocess(cmd: list, env: dict) -> Tuple[int, str]:
203
245
  """
204
246
  Initializes and starts a subprocess using the specified command and provided environment.
205
247
 
248
+ Args:
249
+ cmd (list): The command to execute the subprocess.
250
+ env (dict): The environment variables to set for the subprocess.
251
+
206
252
  Returns:
207
- Optional[int]: The process ID if the process is successfully created, otherwise None.
253
+ Optional[Tuple[int, str]]: A tuple containing the process ID, the URL
254
+ of the server, or None if the process fails to start.
208
255
  """
256
+
209
257
  process = None
210
- mwi_base_url: str = f"{constants.MWI_BASE_URL_PREFIX}{server_id}"
258
+ url = None
211
259
 
212
260
  # Get a free port and corresponding bound socket
213
261
  with helpers.find_free_port() as (port, _):
262
+ log.debug("Allocated port %s", port)
263
+
214
264
  env.update(
215
265
  {
216
266
  "MWI_APP_PORT": port,
217
- "MWI_BASE_URL": mwi_base_url,
218
267
  }
219
268
  )
220
269
 
221
270
  # Using loopback address so that DNS resolution doesn't add latency in Windows
222
- url: str = f"http://127.0.0.1:{port}"
223
-
271
+ url = f"http://127.0.0.1:{port}"
224
272
  process = await _initialize_process_based_on_os_type(cmd, env)
225
-
226
- if not process:
227
- log.error("Matlab proxy process not created due to some error")
228
- return None
229
-
230
- process_pid = process.pid
231
- log.debug("MATLAB proxy info: pid = %s, rc = %s", process_pid, process.returncode)
232
- return process_pid, url, mwi_base_url
273
+ process_pid = process.pid
274
+ log.debug(
275
+ "MATLAB proxy info: pid = %s, returncode = %s",
276
+ process_pid,
277
+ process.returncode,
278
+ )
279
+ return process_pid, url
233
280
 
234
281
 
235
282
  async def _initialize_process_based_on_os_type(cmd, env):
@@ -240,19 +287,18 @@ async def _initialize_process_based_on_os_type(cmd, env):
240
287
  environment variables. It handles both POSIX and Windows systems differently.
241
288
 
242
289
  Args:
243
- cmd (List[str]): The command to execute in the subprocess.
244
- env (Dict[str, str]): The environment variables for the subprocess.
290
+ cmd (list): The command to execute the subprocess.
291
+ env (dict): The environment variables to set for the subprocess.
245
292
 
246
293
  Returns:
247
- Union[Process, None, Popen[bytes]]: The created subprocess object if successful,
248
- or None if an error occurs during subprocess creation.
294
+ subprocess.Popen or asyncio.subprocess.Process: The process object for the started subprocess.
249
295
 
250
296
  Raises:
251
- Exception: If there's an error creating the subprocess (caught and logged).
297
+ exceptions.ProcessStartError: If the subprocess fails to start.
252
298
  """
253
- if mwi_sys.is_posix():
254
- log.debug("Starting matlab proxy subprocess for posix")
255
- try:
299
+ try:
300
+ if mwi_sys.is_posix():
301
+ log.debug("Starting matlab proxy subprocess for posix")
256
302
  return await asyncio.create_subprocess_exec(
257
303
  *cmd,
258
304
  env=env,
@@ -262,19 +308,15 @@ async def _initialize_process_based_on_os_type(cmd, env):
262
308
  # https://github.com/ipython/ipykernel/blob/main/ipykernel/kernelbase.py#L1283
263
309
  start_new_session=True,
264
310
  )
265
- except Exception as e:
266
- log.error("Failed to create posix subprocess: %s", e)
267
- return None
268
- else:
269
- try:
311
+ else:
270
312
  log.debug("Starting matlab proxy subprocess for windows")
271
313
  return subprocess.Popen(
272
314
  cmd,
273
315
  env=env,
274
316
  )
275
- except Exception as e:
276
- log.error("Failed to create windows subprocess: %s", e)
277
- return None
317
+ except Exception as e:
318
+ log.error("Failed to create matlab-proxy subprocess: %s", e)
319
+ raise exceptions.ProcessStartError(extra_info=str(e)) from e
278
320
 
279
321
 
280
322
  async def shutdown(parent_pid: str, caller_id: str, mpm_auth_token: str):
@@ -305,10 +347,10 @@ async def shutdown(parent_pid: str, caller_id: str, mpm_auth_token: str):
305
347
  )
306
348
  return
307
349
 
350
+ filename = f"{parent_pid}_{caller_id}"
308
351
  try:
309
352
  data_dir = helpers.create_and_get_proxy_manager_data_dir()
310
353
  storage = FileRepository(data_dir)
311
- filename = f"{parent_pid}_{caller_id}"
312
354
  full_file_path, server = storage.get(filename)
313
355
 
314
356
  if not server:
@@ -0,0 +1,54 @@
1
+ # MATLAB Proxy Manager - Storage
2
+
3
+ This README is intended for MathWorks® developers only.
4
+ The storage module is a critical part of the `matlab-proxy-manager`, responsible for managing the persistence of metadata related to MATLAB proxy instances. It employs a repository pattern to provide a clean and consistent interface for performing CRD (Create, Read, Delete) operations on the file system.
5
+
6
+ ## Key Features
7
+
8
+ ### Repository Pattern:
9
+
10
+ The storage module is designed using the repository pattern, which abstracts the data layer and provides a straightforward API for interacting with stored metadata. This pattern ensures that the underlying data storage mechanism can be modified or replaced with minimal impact on the rest of the application.
11
+
12
+ ### File System-Based Storage:
13
+
14
+ Currently, the storage operations are performed directly on the file system. Each MATLAB proxy instance's metadata is stored in a separate file, making it easy to manage and access individual instances.
15
+
16
+ ### CRD Operations:
17
+
18
+ The storage module provides a set of APIs to perform CRD operations:
19
+
20
+ 1. add(...): Create a new metadata file for a MATLAB proxy instance.
21
+ 2. get(...): Retrieve metadata for a specific instance.
22
+ 3. get_all(): Retrieve metadata for all instances.
23
+ 4. delete(...): Remove the metadata file for a specific instance.
24
+
25
+ Usage
26
+ To use the storage APIs, clients can import the relevant module and invoke the provided functions. Here’s an example of how to perform basic CRUD operations:
27
+
28
+ ```python
29
+
30
+ from matlab_proxy_manager.storage.file_repository import FileRepository
31
+
32
+ storage = FileRepository(data_dir)
33
+
34
+ # Add a new MATLAB proxy instance metadata
35
+ filename = '1234.info'
36
+ server_process = <instance of ServerProcess class>
37
+ storage.add(server=server_process, filename=filename)
38
+
39
+ # Retrieve metadata for a specific instance
40
+ filename = f"{parent_pid}_{caller_id}"
41
+ full_file_path, server = storage.get(filename)
42
+
43
+ # Retrieve metadata for all instances
44
+ servers = storage.get_all()
45
+
46
+ # Delete metadata for a specific instance
47
+ storage.delete(f"{filename}.info")
48
+ ```
49
+
50
+ ---
51
+
52
+ Copyright 2024 The MathWorks, Inc.
53
+
54
+ ---
@@ -101,8 +101,11 @@ class ServerProcess:
101
101
 
102
102
  # Force kill matlab-proxy and its process tree if termination
103
103
  # via shutdown_integration endpoint fails
104
- matlab_proxy_process = psutil.Process(int(self.pid))
105
- self.terminate_process_tree(matlab_proxy_process)
104
+ try:
105
+ matlab_proxy_process = psutil.Process(int(self.pid))
106
+ self.terminate_process_tree(matlab_proxy_process)
107
+ except Exception as e:
108
+ log.debug("Exception while terminating child processes: %s", e)
106
109
  return None
107
110
 
108
111
  def terminate_process_tree(self, matlab_proxy_process):
@@ -1,5 +1,6 @@
1
- # Copyright 2024 The MathWorks, Inc.
1
+ # Copyright 2024-2025 The MathWorks, Inc.
2
2
 
3
3
  MWI_BASE_URL_PREFIX = "/matlab/"
4
+ MWI_DEFAULT_MATLAB_PATH = MWI_BASE_URL_PREFIX + "default"
4
5
  HEADER_MWI_MPM_CONTEXT = "MWI-MPM-CONTEXT"
5
6
  HEADER_MWI_MPM_AUTH_TOKEN = "MWI-MPM-AUTH-TOKEN"
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2024 The MathWorks, Inc.
1
+ # Copyright 2020-2025 The MathWorks, Inc.
2
2
  """This file lists and exposes the environment variables which are used by proxy manager."""
3
3
 
4
4
  import os
@@ -41,6 +41,11 @@ def get_env_name_mwi_mpm_parent_pid():
41
41
  return "MWI_MPM_PARENT_PID"
42
42
 
43
43
 
44
+ def get_env_name_base_url_prefix():
45
+ """Used to specify the base url prefix for setting base url on matlab (e.g. Jupyter base url)"""
46
+ return "MWI_MPM_BASE_URL_PREFIX"
47
+
48
+
44
49
  def is_web_logging_enabled():
45
50
  """Returns true if the web logging is required to be enabled"""
46
51
  return _is_env_set_to_true(get_env_name_enable_web_logging())
@@ -0,0 +1,45 @@
1
+ # Copyright 2025 The MathWorks, Inc.
2
+
3
+
4
+ class MATLABProxyError(Exception):
5
+ """Base class for all MATLAB Proxy Manager exceptions."""
6
+
7
+ pass
8
+
9
+
10
+ class ProcessStartError(MATLABProxyError):
11
+ """Exception thrown when MATLAB proxy process fails to start."""
12
+
13
+ def __init__(
14
+ self, message="Failed to create matlab-proxy subprocess.", extra_info=None
15
+ ):
16
+ self.message = message
17
+ self.extra_info = extra_info
18
+ super().__init__(message)
19
+
20
+ def __str__(self):
21
+ return (
22
+ f"{self.message} Additional info: {self.extra_info}"
23
+ if self.extra_info
24
+ else self.message
25
+ )
26
+
27
+
28
+ class ServerReadinessError(MATLABProxyError):
29
+ """Exception thrown when MATLAB proxy server fails to become ready"""
30
+
31
+ def __init__(
32
+ self,
33
+ message="MATLAB Proxy Server unavailable: matlab-proxy-app failed to start or has timed out.",
34
+ extra_info=None,
35
+ ):
36
+ self.message = message
37
+ self.extra_info = extra_info
38
+ super().__init__(message)
39
+
40
+ def __str__(self):
41
+ return (
42
+ f"{self.message} Additional info: {self.extra_info}"
43
+ if self.extra_info
44
+ else self.message
45
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2024 The MathWorks, Inc.
1
+ # Copyright 2024-2025 The MathWorks, Inc.
2
2
  import http
3
3
  import os
4
4
  import socket
@@ -204,7 +204,7 @@ def poll_for_server_deletion() -> None:
204
204
  Logs the status of server deletion attempts.
205
205
  """
206
206
  timeout_in_seconds: int = 2
207
- log.info("Interrupt/termination signal caught, cleaning up resources")
207
+ log.debug("Interrupt/termination signal caught, cleaning up resources")
208
208
  start_time = time.time()
209
209
 
210
210
  while time.time() - start_time < timeout_in_seconds:
@@ -1,4 +1,4 @@
1
- # Copyright 2024 The MathWorks, Inc.
1
+ # Copyright 2024-2025 The MathWorks, Inc.
2
2
  # Helper functions to access & control the logging behavior of the app
3
3
 
4
4
  import logging
@@ -56,6 +56,9 @@ def __set_logging_configuration():
56
56
  # also print their logs at the specified level
57
57
  logging.basicConfig(level=log_level)
58
58
 
59
+ # Suppress debug logs from the watchdog module
60
+ logging.getLogger("watchdog").setLevel(logging.WARNING)
61
+
59
62
  return logger
60
63
 
61
64
 
@@ -0,0 +1,37 @@
1
+ # MATLAB Proxy Manager - Web
2
+
3
+ This README is intended for MathWorks&reg; developers only.
4
+ The web module is an important component of the matlab-proxy-manager, responsible for initiating the proxy manager in process mode. This module is specifically designed to be utilized within the Jupyter ecosystem, facilitating seamless integration with Jupyter Server Proxy.
5
+
6
+ ## Key Features
7
+ ### Process Mode Execution:
8
+
9
+ The web module contains the code necessary to start the MATLAB proxy manager in process mode. This mode is essential for managing MATLAB sessions within a Jupyter environment, allowing users to interact with MATLAB through a web-based interface.
10
+
11
+ ### Integration with Jupyter Server Proxy:
12
+
13
+ The web module is accessed exclusively by the Jupyter Server Proxy. It ensures that MATLAB can be launched and managed effectively as part of a Jupyter session, providing a seamless user experience.
14
+
15
+ ### Environment Variable Configuration:
16
+
17
+ The web module relies on three critical environment variables to function correctly:
18
+ 1. MWI_MPM_PORT: Specifies the port on which the proxy manager should start. This allows for flexible configuration and ensures that the proxy manager can be accessed on the appropriate network endpoint.
19
+ 2. MWI_MPM_AUTH_TOKEN: Used for secure communication between the Jupyter Server Proxy and the proxy manager. This token ensures that only authorized requests can interact with the proxy manager, enhancing security.
20
+ 3. MWI_MPM_PARENT_PID: Provides context for the process, allowing resources to be filtered based on their originating parent process. This is particularly useful for distinguishing resources started by different Jupyter servers.
21
+
22
+ ### Support for Proxy Workflow:
23
+
24
+ The web module supports the proxy workflow, which is activated when the MATLAB Web Desktop is launched. This workflow ensures that users can access MATLAB's graphical interface through their web browser, maintaining a consistent and interactive experience.
25
+
26
+ ## Usage
27
+ The web module is typically invoked by the Jupyter Server Proxy and does not require direct interaction from end-users. However, it is essential to ensure that the necessary environment variables are correctly set before launching the module.
28
+
29
+ ## Design Considerations
30
+ 1. Security: The use of an authentication token ensures secure communication between components, preventing unauthorized access.
31
+ 2. Flexibility: By leveraging environment variables, the web module can be easily configured to meet the specific needs of different deployment environments.
32
+
33
+ ---
34
+
35
+ Copyright 2024 The MathWorks, Inc.
36
+
37
+ ---