matlab-proxy 0.26.0__py3-none-any.whl → 0.27.1__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 (58) hide show
  1. matlab_proxy/app.py +50 -39
  2. matlab_proxy/app_state.py +9 -3
  3. matlab_proxy/default_configuration.py +2 -2
  4. matlab_proxy/gui/index.html +2 -2
  5. matlab_proxy/gui/static/css/{index.BSVLACuY.css → index.BLxKpbak.css} +2 -2
  6. matlab_proxy/gui/static/js/{index.CZgGkMCD.js → index.CKi3IRxe.js} +16 -16
  7. matlab_proxy/settings.py +5 -5
  8. matlab_proxy/util/__init__.py +2 -2
  9. matlab_proxy/util/list_servers.py +2 -2
  10. matlab_proxy/util/mwi/environment_variables.py +5 -0
  11. matlab_proxy/util/mwi/logger.py +1 -2
  12. matlab_proxy/util/mwi/session_name.py +28 -0
  13. matlab_proxy/util/mwi/token_auth.py +2 -2
  14. matlab_proxy/util/mwi/validators.py +3 -5
  15. {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.1.dist-info}/METADATA +37 -25
  16. {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.1.dist-info}/RECORD +23 -53
  17. {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.1.dist-info}/WHEEL +1 -2
  18. matlab_proxy_manager/README.md +85 -0
  19. matlab_proxy_manager/lib/README.md +53 -0
  20. matlab_proxy_manager/storage/README.md +54 -0
  21. matlab_proxy_manager/web/README.md +37 -0
  22. matlab_proxy-0.26.0.dist-info/top_level.txt +0 -3
  23. tests/integration/__init__.py +0 -1
  24. tests/integration/integration_tests_with_license/__init__.py +0 -1
  25. tests/integration/integration_tests_with_license/conftest.py +0 -47
  26. tests/integration/integration_tests_with_license/test_http_end_points.py +0 -397
  27. tests/integration/integration_tests_without_license/__init__.py +0 -1
  28. tests/integration/integration_tests_without_license/conftest.py +0 -116
  29. tests/integration/integration_tests_without_license/test_matlab_is_down_if_unlicensed.py +0 -49
  30. tests/integration/utils/__init__.py +0 -1
  31. tests/integration/utils/integration_tests_utils.py +0 -352
  32. tests/integration/utils/licensing.py +0 -152
  33. tests/unit/__init__.py +0 -1
  34. tests/unit/conftest.py +0 -66
  35. tests/unit/test_app.py +0 -1299
  36. tests/unit/test_app_state.py +0 -1094
  37. tests/unit/test_constants.py +0 -7
  38. tests/unit/test_ddux.py +0 -22
  39. tests/unit/test_devel.py +0 -246
  40. tests/unit/test_non_dev_mode.py +0 -169
  41. tests/unit/test_settings.py +0 -679
  42. tests/unit/util/__init__.py +0 -3
  43. tests/unit/util/mwi/__init__.py +0 -1
  44. tests/unit/util/mwi/embedded_connector/__init__.py +0 -1
  45. tests/unit/util/mwi/embedded_connector/test_helpers.py +0 -29
  46. tests/unit/util/mwi/embedded_connector/test_request.py +0 -64
  47. tests/unit/util/mwi/test_custom_http_headers.py +0 -281
  48. tests/unit/util/mwi/test_download.py +0 -152
  49. tests/unit/util/mwi/test_logger.py +0 -82
  50. tests/unit/util/mwi/test_token_auth.py +0 -303
  51. tests/unit/util/mwi/test_validators.py +0 -364
  52. tests/unit/util/test_cookie_jar.py +0 -252
  53. tests/unit/util/test_mw.py +0 -550
  54. tests/unit/util/test_util.py +0 -221
  55. tests/utils/__init__.py +0 -1
  56. tests/utils/logging_util.py +0 -81
  57. {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.1.dist-info}/entry_points.txt +0 -0
  58. {matlab_proxy-0.26.0.dist-info → matlab_proxy-0.27.1.dist-info/licenses}/LICENSE.md +0 -0
@@ -1,352 +0,0 @@
1
- # Copyright 2023-2024 The MathWorks, Inc.
2
- # Utility functions for integration testing of matlab-proxy
3
-
4
- import asyncio
5
- import os
6
- import socket
7
- import time
8
- import urllib3
9
- import requests
10
- import json
11
- from tests.utils.logging_util import create_integ_test_logger
12
-
13
- _logger = create_integ_test_logger(__name__)
14
-
15
-
16
- def perform_basic_checks():
17
- """
18
- Perform basic checks for the prerequisites for starting
19
- matlab-proxy
20
- """
21
- import matlab_proxy.settings
22
-
23
- _logger.info("Performing basic checks for matlab-proxy")
24
-
25
- # Validate MATLAB before testing
26
- _, matlab_path = matlab_proxy.settings.get_matlab_executable_and_root_path()
27
-
28
- # Check if MATLAB is in the system path
29
- assert matlab_path is not None, "MATLAB is not in system path"
30
-
31
- # Check if MATLAB verison is >= R2020b
32
- assert (
33
- matlab_proxy.settings.get_matlab_version(matlab_path) >= "R2020b"
34
- ), "MATLAB version should be R2020b or later"
35
- _logger.debug("Exiting perform_basic_checks")
36
-
37
-
38
- def matlab_proxy_cmd_for_testing():
39
- """
40
- Get command for starting matlab-proxy process
41
-
42
- Returns:
43
- list(string): Command for starting matlab-proxy process
44
- """
45
-
46
- import matlab_proxy
47
-
48
- matlab_cmd = [matlab_proxy.get_executable_name()]
49
-
50
- return matlab_cmd
51
-
52
-
53
- async def start_matlab_proxy_app(out=asyncio.subprocess.PIPE, input_env={}):
54
- """
55
- Starts matlab-proxy as a subprocess. The subprocess runs forever unless
56
- there is any error
57
-
58
- Args:
59
- input_env (dict, optional): Environment variables to be
60
- initialized for the subprocess. Defaults to {}.
61
-
62
- Returns:
63
- Process: subprocess object
64
- """
65
- from matlab_proxy.util import system
66
-
67
- _logger.info("Starting MATLAB Proxy app")
68
- cmd = matlab_proxy_cmd_for_testing()
69
- matlab_proxy_env = os.environ.copy()
70
- matlab_proxy_env.update(input_env)
71
-
72
- proc = await asyncio.create_subprocess_exec(
73
- *cmd,
74
- env=matlab_proxy_env,
75
- stdout=out,
76
- stderr=out,
77
- )
78
- _logger.debug("MATLAB Proxy App started")
79
- return proc
80
-
81
-
82
- def send_http_request(
83
- connection_scheme,
84
- mwi_app_port,
85
- mwi_base_url="",
86
- http_endpoint="",
87
- method="GET",
88
- headers={},
89
- ):
90
- """Send HTTP request to matlab-proxy server.
91
- Returns HTTP response JSON"""
92
-
93
- uri = (
94
- f"{connection_scheme}://localhost:{mwi_app_port}{mwi_base_url}/{http_endpoint}"
95
- )
96
-
97
- with urllib3.PoolManager(
98
- retries=urllib3.Retry(backoff_factor=0.1, backoff_max=300)
99
- ) as http:
100
- res = http.request(method, uri, fields=headers)
101
- return json.loads(res.data.decode("utf-8"))
102
-
103
-
104
- def wait_matlab_proxy_ready(matlab_proxy_url):
105
- """
106
- Wait for matlab-proxy to be up and running
107
-
108
- Args:
109
- matlab_proxy_url (string): URL to access matlab-proxy
110
- """
111
-
112
- from matlab_proxy.util import system
113
- import matlab_proxy.settings as settings
114
-
115
- _logger.info("Wait for MATLAB Proxy to start")
116
- # Wait until the matlab config file is created
117
- MAX_TIMEOUT = settings.get_process_startup_timeout()
118
- start_time = time.time()
119
-
120
- while not os.path.exists(str(get_matlab_config_file())) and (
121
- time.time() - start_time < MAX_TIMEOUT
122
- ):
123
- time.sleep(1)
124
-
125
- try:
126
- if not os.path.exists(str(get_matlab_config_file())):
127
- raise FileNotFoundError("Config file is not present on the path")
128
-
129
- except FileNotFoundError as e:
130
- _logger.exception("Config file does not exist")
131
- _logger.exception(e)
132
-
133
-
134
- def get_random_free_port() -> str:
135
- """
136
- Get a random free port
137
-
138
- Returns:
139
- string: A random free port
140
- """
141
-
142
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
143
- s.bind(("", 0))
144
- port = str(s.getsockname()[1])
145
- s.close()
146
- return port
147
-
148
-
149
- def wait_server_info_ready(port_number):
150
- """
151
- Waits for server file to be available on the disk
152
- """
153
-
154
- import matlab_proxy.settings as settings
155
-
156
- # timeout_value is in seconds
157
- timeout_value = 60
158
- start_time = time.time()
159
- config_folder = settings.get_mwi_config_folder()
160
- _path = config_folder / "ports" / port_number / "mwi_server.info"
161
-
162
- while time.time() - start_time < timeout_value:
163
- if _path.exists():
164
- return True
165
-
166
- time.sleep(1)
167
-
168
- raise FileNotFoundError("mwi_server.info file does not exist")
169
-
170
-
171
- def license_matlab_proxy(matlab_proxy_url):
172
- """
173
- Use Playwright UI automation to license matlab-proxy.
174
- Uses TEST_USERNAME and TEST_PASSWORD from environment variables.
175
-
176
- Args:
177
- matlab_proxy_url (string): URL to access matlab-proxy
178
- """
179
- from playwright.sync_api import sync_playwright, expect
180
-
181
- _logger.info("Licensing MATLAB using matlab-proxy")
182
- # These are MathWorks Account credentials to license MATLAB
183
- # Throws 'KeyError' if the following environment variables are not set
184
- TEST_USERNAME = os.environ["TEST_USERNAME"]
185
- TEST_PASSWORD = os.environ["TEST_PASSWORD"]
186
-
187
- with sync_playwright() as playwright:
188
- browser = playwright.chromium.launch(headless=True)
189
- context = browser.new_context(ignore_https_errors=True)
190
-
191
- page = context.new_page()
192
-
193
- page.goto(matlab_proxy_url)
194
-
195
- # Find the MHLM licensing windows in matlab-proxy
196
- mhlm_div = page.locator("#MHLM")
197
- expect(
198
- mhlm_div,
199
- "Wait for MHLM licensing window to appear. This might fail if the MATLAB is already licensed",
200
- ).to_be_visible(timeout=60000)
201
-
202
- # The login iframe is present within the MHLM Div
203
- login_iframe = mhlm_div.frame_locator("#loginframe")
204
-
205
- # Fills in the username textbox
206
- email_text_box = login_iframe.locator("#userId")
207
- expect(
208
- email_text_box,
209
- "Wait for email ID textbox to appear",
210
- ).to_be_visible(timeout=20000)
211
- email_text_box.fill(TEST_USERNAME)
212
- email_text_box.press("Enter")
213
-
214
- # Fills in the password textbox
215
- password_text_box = login_iframe.locator("#password")
216
- expect(password_text_box, "Wait for password textbox to appear").to_be_visible(
217
- timeout=20000
218
- )
219
- password_text_box.fill(TEST_PASSWORD)
220
- password_text_box.press("Enter")
221
-
222
- # Verifies if licensing is successful by checking the status information
223
- status_info = page.get_by_text("Status Information")
224
- expect(
225
- status_info,
226
- "Verify if Licensing is successful. This might fail if incorrect credentials are provided",
227
- ).to_be_visible(timeout=60000)
228
- _logger.debug("Succeeded in licensing MATLAB using matlab-proxy")
229
- browser.close()
230
-
231
-
232
- def unlicense_matlab_proxy(matlab_proxy_url):
233
- """
234
- Unlicense matlab-proxy that is licensed using online licensing
235
-
236
- Args:
237
- matlab_proxy_url (string): URL to access matlab-proxy
238
- """
239
- import warnings
240
-
241
- _logger.info("Unlicensing matlab-proxy")
242
- max_retries = 3 # Max retries for unlicensing matlab-proxy
243
- retries = 0
244
-
245
- while retries < max_retries:
246
- error = None
247
- try:
248
- resp = requests.delete(
249
- matlab_proxy_url + "/set_licensing_info", headers={}, verify=False
250
- )
251
-
252
- if resp.status_code == requests.codes.OK:
253
- data = resp.json()
254
- assert data["licensing"] == None, "matlab-proxy licensing is not unset"
255
- assert (
256
- data["matlab"]["status"] == "down"
257
- ), "matlab-proxy is not in 'stopped' state"
258
-
259
- # Throw warning if matlab-proxy is unlicensed but with some error
260
- if data["error"] != None:
261
- warnings.warn(
262
- f"matlab-proxy is unlicensed but with error: {data['error']}",
263
- UserWarning,
264
- )
265
- _logger.debug("Succeeded in unlicensing matlab-proxy")
266
- break
267
- else:
268
- resp.raise_for_status()
269
- except Exception as e:
270
- error = e
271
- finally:
272
- retries += 1
273
-
274
- # If the above code threw error even after maximum retries, then raise error
275
- if error:
276
- raise error
277
-
278
-
279
- def get_connection_string(port_number):
280
- """Returns the scheme on which matlab proxy starts (http or https)
281
-
282
- Args:
283
- port_number (string): Port on which MATLAB proxy starts
284
-
285
- Returns:
286
- scheme: String representing the scheme
287
- """
288
- import matlab_proxy.settings as settings
289
-
290
- config_folder = settings.get_mwi_config_folder()
291
- _path = config_folder / "ports" / port_number / "mwi_server.info"
292
- conn_string = None
293
-
294
- try:
295
- with open(_path, "r") as _file:
296
- conn_string = _file.readline().rstrip()
297
-
298
- except FileNotFoundError:
299
- _logger.exception(f"{_path} does not exist")
300
-
301
- return conn_string
302
-
303
-
304
- def poll_web_service(url, step=1, timeout=60, ignore_exceptions=None):
305
- """Poll a web service for a 200 response
306
-
307
- Args:
308
- url (string): URL of the web service
309
- step (int, optional): Poll Interval. Defaults to 1 second.
310
- timeout (int, optional): Polling timout. Defaults to 60 seconds.
311
- ignore_exceptions (tuple, optional): The exceptions that need to be ignored
312
- within the polling timout. Defaults to None.
313
-
314
- Raises:
315
- TimeoutError: Error if polling timeout is exceeded
316
-
317
- Returns:
318
- dict: response dictionary object
319
- """
320
- start_time = time.time()
321
- end_time = start_time + timeout
322
-
323
- import matlab_proxy.settings as settings
324
-
325
- config_folder = settings.get_mwi_config_folder()
326
-
327
- while time.time() < end_time:
328
- try:
329
- response = requests.get(url, verify=False)
330
- if response.status_code == 200:
331
- return response
332
-
333
- except Exception as e:
334
- if ignore_exceptions and isinstance(e, ignore_exceptions):
335
- continue # Ignore specified exceptions
336
- time.sleep(step)
337
-
338
- raise TimeoutError(
339
- f"{url} did not return a 200 response within the timeout period {timeout} seconds."
340
- )
341
-
342
-
343
- def get_matlab_config_file():
344
- """
345
- Gets the path to MATLAB config file generated by matlab-proxy
346
-
347
- Returns:
348
- string: MATLAB config file path
349
- """
350
- from matlab_proxy import settings
351
-
352
- return settings.get()["matlab_config_file"]
@@ -1,152 +0,0 @@
1
- # Copyright 2024 The MathWorks, Inc.
2
- import os
3
- import logging
4
- from playwright.sync_api import Page, Error, sync_playwright, expect
5
- from tests.integration.utils import integration_tests_utils as utils
6
- from tests.utils.logging_util import create_integ_test_logger
7
-
8
- # Configure logging
9
- _logger = create_integ_test_logger(__name__)
10
-
11
- TIMEOUTS = {
12
- # Time in milliseconds
13
- "MHLM_VISIBLE": 60 * 1000,
14
- "TEXTBOX_VISIBLE": 5 * 1000,
15
- "MATLAB_STARTS": 3 * 60 * 1000,
16
- }
17
-
18
-
19
- POLL_INTERVAL = 1000
20
-
21
-
22
- def _get_matlab_proxy_url():
23
- # import integration_tests_utils as utils
24
- import matlab_proxy.util
25
-
26
- mwi_app_port = utils.get_random_free_port()
27
- mwi_base_url = "/matlab-test"
28
-
29
- input_env = {
30
- "MWI_APP_PORT": mwi_app_port,
31
- "MWI_BASE_URL": mwi_base_url,
32
- }
33
-
34
- loop = matlab_proxy.util.get_event_loop()
35
- # Run matlab-proxy in the background in an event loop
36
- proc = loop.run_until_complete(utils.start_matlab_proxy_app(input_env=input_env))
37
-
38
- utils.wait_server_info_ready(mwi_app_port)
39
- matlab_proxy_url = utils.get_connection_string(mwi_app_port)
40
- return matlab_proxy_url, proc, loop
41
-
42
-
43
- def license_matlab_proxy():
44
- import requests
45
-
46
- matlab_proxy_url, proc, loop = _get_matlab_proxy_url()
47
-
48
- utils.poll_web_service(
49
- matlab_proxy_url,
50
- step=5,
51
- timeout=120,
52
- ignore_exceptions=(
53
- requests.exceptions.ConnectionError,
54
- requests.exceptions.SSLError,
55
- ),
56
- )
57
-
58
- licensing_with_online_licensing(matlab_proxy_url)
59
- utils.wait_matlab_proxy_ready(matlab_proxy_url)
60
- proc.terminate()
61
- loop.run_until_complete(proc.wait())
62
-
63
-
64
- def licensing_with_online_licensing(matlab_proxy_url):
65
- """
66
- Use Playwright UI automation to license matlab-proxy.
67
- Uses TEST_USERNAME and TEST_PASSWORD from environment variables.
68
-
69
- Args:
70
- matlab_proxy_url (string): URL to access matlab-proxy
71
- """
72
- from playwright.sync_api import sync_playwright, expect
73
-
74
- # These are MathWorks Account credentials to license MATLAB
75
- # Throws 'KeyError' if the following environment variables are not set
76
- TEST_USERNAME = os.environ["TEST_USERNAME"]
77
- TEST_PASSWORD = os.environ["TEST_PASSWORD"]
78
-
79
- playwright, browser, page = _launch_browser()
80
- page.goto(matlab_proxy_url)
81
-
82
- # Find the MHLM licensing window in matlab-proxy
83
- login_iframe = _wait_for_login_iframe(page)
84
-
85
- # Fills in the username textbox
86
- email_text_box = _fill_in_username(TEST_USERNAME, login_iframe)
87
- email_text_box.press("Enter")
88
-
89
- # Fills in the password textbox
90
- password_text_box = _fill_in_password(TEST_PASSWORD, login_iframe)
91
- password_text_box.press("Enter")
92
-
93
- # Verifies if licensing is successful by checking the status information
94
- _verify_licensing(page)
95
- _close_resources(playwright, browser)
96
-
97
-
98
- def _verify_licensing(page):
99
- status_info = page.get_by_text("Status Information")
100
- expect(
101
- status_info,
102
- "Verify if Licensing is successful. This might fail if incorrect credentials are provided",
103
- ).to_be_visible(timeout=TIMEOUTS["MHLM_VISIBLE"])
104
-
105
-
106
- def _wait_for_login_iframe(matlab_proxy_page):
107
- """Waits for the MHLM/Online Licensing form to appear."""
108
- mhlm_div = matlab_proxy_page.locator("#MHLM")
109
- expect(
110
- mhlm_div,
111
- "Wait for MHLM licensing window to appear. This might fail if the MATLAB is already licensed",
112
- ).to_be_visible(timeout=TIMEOUTS["MHLM_VISIBLE"])
113
-
114
- # The login iframe is present within the MHLM Div
115
- login_iframe = mhlm_div.frame_locator("#loginframe")
116
- return login_iframe
117
-
118
-
119
- def _launch_browser(headless: bool = True) -> tuple:
120
- """Launches the browser and returns the browser and page objects."""
121
- playwright = sync_playwright().start()
122
- browser = playwright.chromium.launch(headless=headless)
123
- context = browser.new_context(ignore_https_errors=True)
124
- page = context.new_page()
125
- return playwright, browser, page
126
-
127
-
128
- def _close_resources(playwright, browser):
129
- """Closes the browser and playwright resources properly."""
130
- browser.close()
131
- playwright.stop()
132
-
133
-
134
- def _fill_in_username(username, login_iframe):
135
- """Inputs the provided username string into the MHLM login form."""
136
- email_text_box = login_iframe.locator("#userId")
137
- expect(
138
- email_text_box,
139
- "Wait for email ID textbox to appear",
140
- ).to_be_visible(timeout=TIMEOUTS["TEXTBOX_VISIBLE"])
141
- email_text_box.fill(username)
142
- return email_text_box
143
-
144
-
145
- def _fill_in_password(password, login_iframe):
146
- """Inputs the provided password string into the MHLM login form."""
147
- password_text_box = login_iframe.locator("#password")
148
- expect(password_text_box, "Wait for password textbox to appear").to_be_visible(
149
- timeout=TIMEOUTS["TEXTBOX_VISIBLE"]
150
- )
151
- password_text_box.fill(password)
152
- return password_text_box
tests/unit/__init__.py DELETED
@@ -1 +0,0 @@
1
- # Copyright 2024 The MathWorks, Inc.
tests/unit/conftest.py DELETED
@@ -1,66 +0,0 @@
1
- # Copyright 2020-2022 The MathWorks, Inc.
2
-
3
- """The conftest.py file is run by the pytest framework for setting things up for the test session."""
4
-
5
- import os
6
- import shutil
7
- import asyncio
8
-
9
- import pytest
10
- from matlab_proxy import settings, util
11
- from matlab_proxy.util.mwi import environment_variables as mwi_env
12
-
13
-
14
- def pytest_generate_tests(metafunc):
15
- os.environ[mwi_env.get_env_name_development()] = "true"
16
-
17
-
18
- def __get_matlab_config_file():
19
- return settings.get(dev=True)["matlab_config_file"]
20
-
21
-
22
- def __delete_matlab_config_file():
23
- os.remove(__get_matlab_config_file())
24
-
25
-
26
- @pytest.fixture(autouse=True, scope="session")
27
- def pre_test_cleanup():
28
- """A pytest fixture which deletes matlab_config_file before executing tests.
29
-
30
- If a previous pytest run fails, this file may have an empty Dict which leads
31
- to the server never starting up matlab.
32
- """
33
- try:
34
- __delete_matlab_config_file()
35
- except FileNotFoundError:
36
- pass
37
-
38
-
39
- @pytest.fixture(scope="session", autouse=True)
40
- def cleanup(request):
41
- """Cleanup the temp directory once we are finished."""
42
-
43
- def delete_matlab_test_dir():
44
- # Delete matlab_config_file & its owning directory
45
- matlab_config_file = __get_matlab_config_file()
46
- matlab_config_dir = os.path.dirname(matlab_config_file)
47
- try:
48
- shutil.rmtree(matlab_config_dir)
49
- except FileNotFoundError:
50
- pass
51
-
52
- request.addfinalizer(delete_matlab_test_dir)
53
-
54
-
55
- @pytest.fixture(scope="session")
56
- def event_loop():
57
- """Overriding the default event loop of pytest. Intended for windows systems for
58
- python <= 3.7 where WindowsSelectorEvent Loop is the default event loop. For
59
- python >= 3.8, WindowsProactorEvent Loop is the default.
60
-
61
- Yields:
62
- asyncio.loop: WindowsProactorEvent loop in Windows or UnixSelectorEventLoop in posix.
63
- """
64
- loop = util.get_event_loop()
65
- yield loop
66
- loop.close()