matlab-proxy 0.12.1__py3-none-any.whl → 0.14.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.

matlab_proxy/settings.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # Copyright 2020-2024 The MathWorks, Inc.
2
2
 
3
+ import datetime
3
4
  import os
4
5
  import shutil
5
6
  import socket
@@ -9,6 +10,11 @@ import uuid
9
10
  import xml.etree.ElementTree as ET
10
11
  from pathlib import Path
11
12
 
13
+ from cryptography import x509
14
+ from cryptography.hazmat.primitives import hashes, serialization
15
+ from cryptography.hazmat.primitives.asymmetric import rsa
16
+ from cryptography.x509.oid import NameOID
17
+
12
18
  import matlab_proxy
13
19
  from matlab_proxy import constants
14
20
  from matlab_proxy.util import mwi, system
@@ -289,10 +295,6 @@ def get_server_settings(config_name):
289
295
  mwi_auth_token_hash,
290
296
  ) = token_auth.generate_mwi_auth_token_and_hash().values()
291
297
  mwi_config_folder = get_mwi_config_folder()
292
- ssl_key_file, ssl_cert_file = mwi.validators.validate_ssl_key_and_cert_file(
293
- os.getenv(mwi_env.get_env_name_ssl_key_file(), None),
294
- os.getenv(mwi_env.get_env_name_ssl_cert_file(), None),
295
- )
296
298
 
297
299
  # log file validation check is already done in logger.py
298
300
  mwi_log_file = os.getenv(mwi_env.get_env_name_log_file(), None)
@@ -328,9 +330,7 @@ def get_server_settings(config_name):
328
330
  "mwi_use_existing_license": mwi.validators.validate_use_existing_licensing(
329
331
  os.getenv(mwi_env.get_env_name_mwi_use_existing_license(), "")
330
332
  ),
331
- "ssl_context": get_ssl_context(
332
- ssl_cert_file=ssl_cert_file, ssl_key_file=ssl_key_file
333
- ),
333
+ "ssl_context": _validate_ssl_files_and_get_ssl_context(mwi_config_folder),
334
334
  }
335
335
 
336
336
 
@@ -478,31 +478,127 @@ def get_test_temp_dir():
478
478
  return test_temp_dir
479
479
 
480
480
 
481
- def get_ssl_context(ssl_cert_file, ssl_key_file):
482
- """Creates an SSL CONTEXT for use with the TCP Site"""
481
+ def _validate_ssl_files_and_get_ssl_context(mwi_config_folder):
482
+ """Creates an SSL CONTEXT for use with the TCP Site.
483
+ The certfile string must be the path to a single file in PEM format containing the
484
+ certificate as well as any number of CA certificates needed to establish the certificate’s authenticity.
485
+ The keyfile string, if present, must point to a file containing the private key in.
486
+ Otherwise the private key will be taken from certfile as well.
487
+ """
488
+ is_self_signed_certificates = False
489
+ env_name_enable_ssl = mwi_env.get_env_name_enable_ssl()
490
+ is_ssl_enabled = mwi_env._is_env_set_to_true(env_name_enable_ssl)
491
+ env_name_ssl_key_file = mwi_env.get_env_name_ssl_key_file()
492
+ env_name_ssl_cert_file = mwi_env.get_env_name_ssl_cert_file()
493
+
494
+ ssl_key_file, ssl_cert_file = (
495
+ os.getenv(env_name_ssl_key_file, None),
496
+ os.getenv(env_name_ssl_cert_file, None),
497
+ )
483
498
 
484
- # The certfile string must be the path to a single file in PEM format containing the
485
- # certificate as well as any number of CA certificates needed to establish the certificate’s authenticity.
486
- # The keyfile string, if present, must point to a file containing the private key in.
487
- # Otherwise the private key will be taken from certfile as well.
488
- import traceback
499
+ # Don't use SSL if the user has explicitly disabled SSL communication or not set the respective env var
500
+ if not is_ssl_enabled:
501
+ if ssl_cert_file:
502
+ logger.warn(
503
+ f"Ignoring provided SSL files, as {env_name_enable_ssl} is either unset or set to false"
504
+ )
505
+ return None
489
506
 
490
- if ssl_cert_file != None:
491
- try:
492
- ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
493
- ssl_context.load_cert_chain(ssl_cert_file, ssl_key_file)
494
- logger.debug(f"Using SSL certification!")
495
- except Exception as e:
496
- # Something was wrong with the certificates provided
497
- error_message = "SSL certificates provided are invalid. Aborting..."
498
- logger.error(error_message)
499
- traceback.print_exc()
500
- logger.info("==== Fatal error : ===")
501
- print(e)
502
- # printing stack trace
503
- logger.info("======================")
504
- raise FatalError(error_message)
505
- else:
507
+ # Validate that provided SSL files are valid files
508
+ ssl_key_file, ssl_cert_file = mwi.validators.validate_ssl_key_and_cert_file(
509
+ ssl_key_file, ssl_cert_file
510
+ )
511
+
512
+ if not ssl_cert_file and not ssl_key_file:
513
+ logger.debug("Using auto-generated self-signed certificates")
514
+
515
+ # certs dir under the MWI_CONFIG_FOLDER will hold the self-signed certificates
516
+ mwi_certs_dir = mwi_config_folder / "certs"
517
+ mwi_certs_dir.mkdir(parents=True, exist_ok=True)
518
+
519
+ # New certs are generated for every run leading to functionally reliable system, alternative is
520
+ # to check for existing certs and have error handling around expired/bad certs.
521
+ ssl_cert_file, ssl_key_file = generate_new_self_signed_certs(mwi_certs_dir)
522
+ is_self_signed_certificates = True
523
+ try:
524
+ ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
525
+ ssl_context.load_cert_chain(ssl_cert_file, ssl_key_file)
526
+ logger.debug("Certificate chain was correctly loaded")
527
+ except Exception as e:
528
+ logger.error(f"Unable to load certificates. Error: {e}")
529
+
530
+ # Setting to None to use http mode in the event of failing to setup self-signed certificates
506
531
  ssl_context = None
507
532
 
533
+ # Raise a fatal error only in the event of an exception while loading customer-supplied ssl files
534
+ if not is_self_signed_certificates:
535
+ raise FatalError(e)
536
+
508
537
  return ssl_context
538
+
539
+
540
+ def generate_new_self_signed_certs(mwi_certs_dir):
541
+ """
542
+ Generates a new self-signed certificate and corresponding private key, saves them as PEM files in the specified directory.
543
+ The certificate is valid for 365 days from the time of creation.
544
+
545
+ Parameters:
546
+ - mwi_certs_dir (Path): A pathlib.Path object representing the directory where the certificate and key files will be saved.
547
+
548
+ Returns:
549
+ - tuple: A tuple containing the file paths (as strings) to the newly created certificate and private key PEM files.
550
+ The first element is the path to the certificate file (cert.pem), and the second is the path to the key file (key.pem).
551
+
552
+ Raises:
553
+ - FileNotFoundError: If the mwi_certs_dir does not exist.
554
+ - Any other exception that may occur during file writing or certificate generation.
555
+ """
556
+ cert_file = priv_key_file = None
557
+ try:
558
+ # Generate private key
559
+ private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
560
+
561
+ # Self-signed certificate
562
+ subject = issuer = x509.Name(
563
+ [
564
+ x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
565
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Massachusetts"),
566
+ x509.NameAttribute(NameOID.LOCALITY_NAME, "Natick"),
567
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MathWorks Inc."),
568
+ x509.NameAttribute(NameOID.COMMON_NAME, "mathworks.com"),
569
+ ]
570
+ )
571
+ cert = (
572
+ x509.CertificateBuilder()
573
+ .subject_name(subject)
574
+ .issuer_name(issuer)
575
+ .public_key(private_key.public_key())
576
+ .serial_number(x509.random_serial_number())
577
+ .not_valid_before(datetime.datetime.utcnow())
578
+ .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
579
+ .sign(private_key, hashes.SHA256())
580
+ )
581
+
582
+ # Write private key to file
583
+ priv_key_file = mwi_certs_dir / "key.pem"
584
+ with open(priv_key_file, "wb") as f:
585
+ f.write(
586
+ private_key.private_bytes(
587
+ encoding=serialization.Encoding.PEM,
588
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
589
+ encryption_algorithm=serialization.NoEncryption(),
590
+ )
591
+ )
592
+
593
+ # Write certificate to file
594
+ cert_file = mwi_certs_dir / "cert.pem"
595
+ with open(cert_file, "wb") as f:
596
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
597
+
598
+ except Exception as ex:
599
+ logger.warn(
600
+ f"Failed to generate self-signed certificates, proceeding with non-secure mode! Error: {ex}"
601
+ )
602
+ cert_file = priv_key_file = None
603
+
604
+ return cert_file, priv_key_file
@@ -1,4 +1,5 @@
1
- # Copyright (c) 2020-2023 The MathWorks, Inc.
1
+ # Copyright 2020-2024 The MathWorks, Inc.
2
+
2
3
  import argparse
3
4
  import os
4
5
  import socket
@@ -30,9 +31,18 @@ def parse_cli_args():
30
31
  help="A json file which stores the config specific to the environment.",
31
32
  default=matlab_proxy.get_default_config_name(),
32
33
  )
34
+
35
+ parser.add_argument(
36
+ "-v",
37
+ "--version",
38
+ help="prints the version of matlab-proxy.",
39
+ action="store_true",
40
+ )
41
+
33
42
  args = parser.parse_args()
34
43
 
35
44
  parsed_args["config"] = args.config
45
+ parsed_args["version"] = args.version
36
46
 
37
47
  return parsed_args
38
48
 
matlab_proxy/util/mw.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2023 The MathWorks, Inc.
1
+ # Copyright 2020-2024 The MathWorks, Inc.
2
2
 
3
3
  import asyncio
4
4
  import os
@@ -46,7 +46,7 @@ async def fetch_entitlements(mhlm_api_endpoint, access_token, matlab_release):
46
46
  list: Representing a list of Dicts containing the id, label and license_number.
47
47
  """
48
48
  # Get entitlements for token
49
- async with aiohttp.ClientSession() as client_session:
49
+ async with aiohttp.ClientSession(trust_env=True) as client_session:
50
50
  async with client_session.post(
51
51
  mhlm_api_endpoint,
52
52
  headers={"content-type": "application/x-www-form-urlencoded"},
@@ -99,7 +99,7 @@ async def fetch_expand_token(mwa_api_endpoint, identity_token, source_id):
99
99
  Returns:
100
100
  Dict: Containing User and License expiration details.
101
101
  """
102
- async with aiohttp.ClientSession() as client_session:
102
+ async with aiohttp.ClientSession(trust_env=True) as client_session:
103
103
  async with client_session.post(
104
104
  f"{mwa_api_endpoint}/tokens",
105
105
  headers={
@@ -146,7 +146,7 @@ async def fetch_access_token(mwa_api_endpoint, identity_token, source_id):
146
146
  Returns:
147
147
  Dict : Containing the Access token.
148
148
  """
149
- async with aiohttp.ClientSession() as client_session:
149
+ async with aiohttp.ClientSession(trust_env=True) as client_session:
150
150
  async with client_session.post(
151
151
  f"{mwa_api_endpoint}/tokens/access",
152
152
  headers={
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2023 The MathWorks, Inc.
1
+ # Copyright 2020-2024 The MathWorks, Inc.
2
2
 
3
3
  """
4
4
  This file contains the methods to communicate with the embedded connector.
@@ -42,7 +42,7 @@ async def send_request(url: str, data: dict, method: str, headers: dict = None)
42
42
  data = json.dumps(data)
43
43
 
44
44
  try:
45
- async with aiohttp.ClientSession() as session:
45
+ async with aiohttp.ClientSession(trust_env=True) as session:
46
46
  logger.debug(
47
47
  f"sending request: method={method}, url={url}, data={data}, headers={headers}, "
48
48
  )
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2023 The MathWorks, Inc.
1
+ # Copyright 2020-2024 The MathWorks, Inc.
2
2
  """This file lists and exposes the environment variables which are used by the integration."""
3
3
 
4
4
  import os
@@ -16,6 +16,18 @@ def _is_env_set_to_true(env_name: str) -> bool:
16
16
  return os.environ.get(env_name, "false").lower() == "true"
17
17
 
18
18
 
19
+ def _is_env_set_to_false(env_name: str) -> bool:
20
+ """Helper function that returns True if the environment variable specified is set to False.
21
+
22
+ Args:
23
+ env_name (str): Name of the environment variable to check the state for.
24
+
25
+ Returns:
26
+ bool: True if the value of the environment variable is a case insensitive match to the string "False"
27
+ """
28
+ return os.environ.get(env_name, "").lower() == "false"
29
+
30
+
19
31
  def get_env_name_network_license_manager():
20
32
  """Specifies the path to valid license file or address of a network license server"""
21
33
  return "MLM_LICENSE_FILE"
@@ -105,6 +117,11 @@ def is_web_logging_enabled():
105
117
  return _is_env_set_to_true(get_env_name_enable_web_logging())
106
118
 
107
119
 
120
+ def get_env_name_enable_ssl():
121
+ """Returns the environment variable used for enabling/disabling SSL/TLS communication."""
122
+ return "MWI_ENABLE_SSL"
123
+
124
+
108
125
  def get_env_name_ssl_cert_file():
109
126
  """Specifies the certificate to be used by webserver."""
110
127
  return "MWI_SSL_CERT_FILE"
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2023 The MathWorks, Inc.
1
+ # Copyright 2020-2024 The MathWorks, Inc.
2
2
 
3
3
  # This file contains functions required to enable token based authentication in the server.
4
4
 
@@ -24,49 +24,36 @@ def generate_mwi_auth_token_and_hash():
24
24
  Generate the MWI Token and a hash for that token to be used by the server,
25
25
  based on the environment variables that control it.
26
26
 
27
- If MWI_AUTH_TOKEN is set then assume that the user wants authentication
28
- even if MWI_ENABLE_TOKEN_AUTH is not set. Unless, MWI_ENABLE_TOKEN_AUTH is
29
- explicitly set to FALSE.
27
+ Token Auth is enabled by default, unless MWI_ENABLE_TOKEN_AUTH is explicitly set to False.
30
28
 
31
- If MWI_ENABLE_TOKEN_AUTH is set, and MWI_AUTH_TOKEN is unset, then generate a token.
29
+ If MWI_ENABLE_TOKEN_AUTH is set, and MWI_AUTH_TOKEN is unset, then generate a token
30
+ else if MWI_AUTH_TOKEN is set, use that token for authentication.
32
31
 
33
32
  Returns the Token and its hash to be used for authentication if enabled.
34
33
  Returns None, if Token-Based Authentication is not enabled by user.
35
34
  """
36
- mwi_enable_auth_token = os.getenv(
37
- mwi_env.get_env_name_enable_mwi_auth_token(), None
38
- )
39
-
40
- # Is set explicitly
41
- is_auth_explicitly_disabled = (
42
- mwi_enable_auth_token and mwi_enable_auth_token.lower() == "false"
43
- )
44
- is_auth_explicitly_enabled = (
45
- mwi_enable_auth_token and mwi_enable_auth_token.lower() == "true"
46
- )
47
-
48
- mwi_auth_token = os.getenv(mwi_env.get_env_name_mwi_auth_token(), None)
35
+ env_name_enable_mwi_token_auth = mwi_env.get_env_name_enable_mwi_auth_token()
36
+ env_name_mwi_auth_token = mwi_env.get_env_name_mwi_auth_token()
37
+ enable_token_auth = os.getenv(env_name_enable_mwi_token_auth, "").lower()
38
+ auth_token = os.getenv(env_name_mwi_auth_token, "")
49
39
 
50
- if mwi_auth_token:
51
- if is_auth_explicitly_disabled:
40
+ if enable_token_auth == "false":
41
+ if auth_token:
52
42
  logger.warn(
53
- "Ignoring MWI_AUTH_TOKEN, as MWI_ENABLE_AUTH_TOKEN explicitly set to false"
43
+ f"Ignoring {env_name_mwi_auth_token}, as {env_name_enable_mwi_token_auth} explicitly set to false"
54
44
  )
55
- return _format_token_as_dictionary(None)
56
- else:
57
- # Strip leading and trailing whitespaces if token is not None.
58
- mwi_auth_token = mwi_auth_token.strip()
59
- logger.debug(f"Using provided mwi_auth_token.")
60
- return _format_token_as_dictionary(mwi_auth_token)
61
- else:
62
- if is_auth_explicitly_enabled:
63
- # Generate a url safe token
64
- generated_token = secrets.token_urlsafe()
65
- logger.debug(f"Using auto generated token.")
66
- return _format_token_as_dictionary(generated_token)
67
-
68
- # Return none in all other cases
69
- return _format_token_as_dictionary(None)
45
+ return _format_token_as_dictionary(None)
46
+
47
+ if auth_token:
48
+ auth_token = auth_token.strip()
49
+ logger.debug(f"Using provided {env_name_mwi_auth_token}.")
50
+ return _format_token_as_dictionary(auth_token)
51
+
52
+ # default catch-all for when the env variables are not set or above conditions are not met.
53
+ # This path will be executed if MWI_ENABLE_TOKEN_AUTH is set to true/Any value (default workflow)
54
+ generated_token = secrets.token_urlsafe()
55
+ logger.debug("Using auto-generated token.")
56
+ return _format_token_as_dictionary(generated_token)
70
57
 
71
58
 
72
59
  def get_mwi_auth_token_access_str(app_settings):
@@ -1,5 +1,5 @@
1
- # Copyright 2020-2023 The MathWorks, Inc.
2
- """This file contains validators for various runtime artefacts.
1
+ # Copyright 2020-2024 The MathWorks, Inc.
2
+ """This file contains validators for various runtime artifacts.
3
3
  A validator is defined as a function which verifies the input and
4
4
  returns it unchanged if validation passes.
5
5
  Returning inputs allows validators to be used inline with the input.
@@ -222,55 +222,54 @@ def __get_configs():
222
222
  return configs
223
223
 
224
224
 
225
- def validate_ssl_cert_file(a_ssl_cert_file):
225
+ def validate_ssl_file(ssl_file, env_name):
226
226
  """Ensures that its a valid readable file"""
227
227
 
228
228
  # Empty strings are valid inputs
229
- if a_ssl_cert_file:
230
- # String is not empty, check to see if the file exists
231
- if not os.path.isfile(a_ssl_cert_file):
232
- error_message = f"MWI_SSL_CERT_FILE is not a valid file: {a_ssl_cert_file}"
233
- logger.error(error_message)
234
- raise FatalError(error_message)
229
+ if not ssl_file:
230
+ return None
235
231
 
236
- # string is either empty, or is a valid file on disk
237
- return a_ssl_cert_file
232
+ # String is not empty, check to see if the file exists
233
+ if not os.path.isfile(ssl_file):
234
+ error_message = f"{env_name} is not a valid file: {ssl_file}"
235
+ logger.error(error_message)
236
+ raise FatalError(error_message)
238
237
 
238
+ # string is a valid file on disk
239
+ return ssl_file
239
240
 
240
- def validate_ssl_key_and_cert_file(a_ssl_key_file, a_ssl_cert_file):
241
- """Ensures that its a valid readable file"""
242
241
 
243
- if a_ssl_cert_file is None and a_ssl_key_file is None:
244
- # Both values are None, this is acceptable.
245
- return a_ssl_key_file, a_ssl_cert_file
242
+ def validate_ssl_key_and_cert_file(ssl_key_file, ssl_cert_file):
243
+ """Validates that provided SSL files are valid readable files"""
244
+ env_name_ssl_cert_file = mwi_env.get_env_name_ssl_cert_file()
245
+ env_name_ssl_key_file = mwi_env.get_env_name_ssl_key_file()
246
246
 
247
- # Implies atleast one value is not None.
247
+ if not ssl_cert_file and not ssl_key_file:
248
+ # Both values are falsy, this is acceptable and signify that HTTPS communication is disabled.
249
+ return None, None
248
250
 
249
- # Cert file is either empty or valid file.
250
- cert_file = validate_ssl_cert_file(a_ssl_cert_file=a_ssl_cert_file)
251
+ # Implies at least one value is not falsy.
251
252
 
252
- if cert_file is None and a_ssl_key_file is not None:
253
- error_message = (
254
- f"MWI_SSL_CERT_FILE must be provided to use the MWI_SSL_KEY_FILE"
255
- )
253
+ # Validating cert file- Cert file is either empty or valid file.
254
+ cert_file = validate_ssl_file(
255
+ ssl_file=ssl_cert_file, env_name=env_name_ssl_cert_file
256
+ )
257
+ if not cert_file:
258
+ error_message = f"{env_name_ssl_cert_file} must be provided to use the {env_name_ssl_key_file}"
256
259
  logger.error(error_message)
257
260
  raise FatalError(error_message)
258
261
 
259
- if a_ssl_key_file is None and cert_file is not None:
262
+ # Validating key file
263
+ key_file = validate_ssl_file(ssl_file=ssl_key_file, env_name=env_name_ssl_key_file)
264
+ if not ssl_key_file:
260
265
  logger.info(
261
- f"MWI_SSL_KEY_FILE is not provided, ensure that your MWI_SSL_CERT_FILE : '{cert_file}' contains a private key"
266
+ f"{env_name_ssl_key_file} is not provided, ensure that your {env_name_ssl_cert_file} : '{cert_file}' contains a private key"
262
267
  )
263
268
 
264
- if a_ssl_key_file:
265
- if not os.path.isfile(a_ssl_key_file):
266
- error_message = f"MWI_SSL_KEY_FILE is not a valid file: {a_ssl_key_file}"
267
- logger.error(error_message)
268
- raise FatalError(error_message)
269
-
270
269
  logger.info(
271
- f"SSL Keys provided were: MWI_SSL_CERT_FILE: {a_ssl_cert_file} & MWI_SSL_KEY_FILE: {a_ssl_key_file}"
270
+ f"SSL Keys provided were: {env_name_ssl_cert_file}: {cert_file} & {env_name_ssl_key_file}: {key_file}"
272
271
  )
273
- return a_ssl_key_file, a_ssl_cert_file
272
+ return key_file, cert_file
274
273
 
275
274
 
276
275
  def validate_use_existing_licensing(use_existing_license):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: matlab-proxy
3
- Version: 0.12.1
3
+ Version: 0.14.0
4
4
  Summary: Python® package enables you to launch MATLAB® and access it from a web browser.
5
5
  Home-page: https://github.com/mathworks/matlab-proxy/
6
6
  Author: The MathWorks, Inc.
@@ -26,6 +26,7 @@ Requires-Dist: black ; extra == 'dev'
26
26
  Requires-Dist: pytest ; extra == 'dev'
27
27
  Requires-Dist: pytest-env ; extra == 'dev'
28
28
  Requires-Dist: pytest-cov ; extra == 'dev'
29
+ Requires-Dist: pytest-timeout ; extra == 'dev'
29
30
  Requires-Dist: pytest-mock ; extra == 'dev'
30
31
  Requires-Dist: pytest-aiohttp ; extra == 'dev'
31
32
  Requires-Dist: psutil ; extra == 'dev'
@@ -36,8 +37,6 @@ Requires-Dist: pytest-playwright ; extra == 'dev'
36
37
  # MATLAB Proxy
37
38
  [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/mathworks/matlab-proxy/run-tests.yml?branch=main&logo=github)](https://github.com/mathworks/matlab-proxy/actions)   [![PyPI badge](https://img.shields.io/pypi/v/matlab-proxy.svg?logo=pypi)](https://pypi.python.org/pypi/matlab-proxy)   [![codecov](https://codecov.io/gh/mathworks/matlab-proxy/branch/main/graph/badge.svg?token=ZW3SESKCSS)](https://codecov.io/gh/mathworks/matlab-proxy)   [![Downloads](https://static.pepy.tech/personalized-badge/matlab-proxy?period=month&units=international_system&left_color=grey&right_color=blue&left_text=PyPI%20downloads/month)](https://pepy.tech/project/matlab-proxy)
38
39
 
39
- Copyright (c) 2020-2023 The MathWorks, Inc. All rights reserved.
40
-
41
40
  ----
42
41
 
43
42
  `matlab-proxy` is a Python® package which enables you to launch MATLAB® and access it from a web browser.
@@ -130,7 +129,7 @@ Once the `matlab-proxy` package is installed.
130
129
 
131
130
  Running the above command will print text out on your terminal, which will contain the URL to access MATLAB. For example:
132
131
  ```
133
- MATLAB can be accessed on
132
+ Access MATLAB at
134
133
  http://localhost:44549/matlab/index.html
135
134
  ```
136
135
 
@@ -198,6 +197,10 @@ Install the version >=0.5.0 to use the package on MacOS.
198
197
  $ pip install --upgrade matlab-proxy>=0.5.0
199
198
  ```
200
199
 
200
+ ### Windows Subsystem for Linux (WSL 2)
201
+
202
+ To install `matlab-proxy` in WSL 2, follow the steps mentioned in the [Installation Guide for WSL 2](./installation/wsl2/README.md).
203
+
201
204
  ## Using an already activated MATLAB with matlab-proxy
202
205
  `matlab-proxy` version `v0.7.0` introduces support for using an existing MATLAB license. Use the Existing License option only if you have an activated MATLAB. This allows you to start MATLAB without authenticating every time.
203
206
 
@@ -218,3 +221,10 @@ Using the latest version will provide the latest available security updates or p
218
221
  We encourage you to try this repository with your environment and provide feedback.
219
222
  If you encounter a technical issue or have an enhancement request, create an issue [here](https://github.com/mathworks/matlab-proxy/issues)
220
223
 
224
+ ---
225
+
226
+ Copyright 2020-2024 The MathWorks, Inc.
227
+
228
+ ---
229
+
230
+
@@ -1,14 +1,14 @@
1
1
  matlab_proxy/__init__.py,sha256=6cwi8buKCMtw9OeWaOYUHEoqwl5MyJ_s6GxgNuqPuNg,1673
2
- matlab_proxy/app.py,sha256=OmZZPMjdJ-H9JE6js_zjTvlcTe7QljYBTQHy9rsFMV8,31023
3
- matlab_proxy/app_state.py,sha256=kDAS4QxFuft-yWxApO7NE66mehKuN5makbG66PrT0PU,55381
2
+ matlab_proxy/app.py,sha256=Fd8kbdLF72LHZq_QptKeQc1Qti4TTF1kOJh_Sa1hd3U,32260
3
+ matlab_proxy/app_state.py,sha256=HV4_igTkUVf-CQWPKc7s3z4Ea_MkSBbV7k3I-AG-m28,55374
4
4
  matlab_proxy/constants.py,sha256=GTuIP6ibk1HcoxVihBRcy7SOfoQdb4hy0vhj2nH99sc,910
5
5
  matlab_proxy/default_configuration.py,sha256=DxQaHzAivzstiPl_nDfxs8SOyP9oaK9v3RP4LtroJl4,843
6
6
  matlab_proxy/devel.py,sha256=nR6XPVBUEdQ-RZGtYvX1YHTp8gj9cuw5Hp8ahasMBc8,14310
7
- matlab_proxy/settings.py,sha256=0LH5JwJ2kPRAEXlxEdjE7YJaMU4QN81T2XLjvQ9YNu0,20485
7
+ matlab_proxy/settings.py,sha256=YjchyZAzvLfl-CiwxAPtSaTTo7aRgD6inS4-jZmsuNY,24687
8
8
  matlab_proxy/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- matlab_proxy/gui/asset-manifest.json,sha256=lvZ3--sHfk4-SWhmkhIObN68EzK_RtGdbjvtafB2KdA,3516
9
+ matlab_proxy/gui/asset-manifest.json,sha256=0eynKD0QHNdOgsiH3IuXXNkRQph4sd_QV4S89qocW2k,3516
10
10
  matlab_proxy/gui/favicon.ico,sha256=7w7Ki1uQP2Rgwc64dOV4-NrTu97I3WsZw8OvRSoY1A0,130876
11
- matlab_proxy/gui/index.html,sha256=tCPDXJyqFbhFhicBGQPnODu4nmnMiV6NEfXoB47zMjg,637
11
+ matlab_proxy/gui/index.html,sha256=IxsvWXcW8bVcVPdnnMQoolow1BFirTUyCo-O9uYGYTQ,637
12
12
  matlab_proxy/gui/manifest.json,sha256=NwDbrALM5auYyj2bbEf4aGwAUDqNl1FzMFQpPiG2Ty4,286
13
13
  matlab_proxy/gui/robots.txt,sha256=kNJLw79pisHhc3OVAimMzKcq3x9WT6sF9IS4xI0crdI,67
14
14
  matlab_proxy/gui/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -16,9 +16,9 @@ matlab_proxy/gui/static/css/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
16
16
  matlab_proxy/gui/static/css/main.47712126.css,sha256=OT5ccyC0a6fVg956C-8b6uDVbZZVgcpXbHjdO6v4ZBI,274313
17
17
  matlab_proxy/gui/static/css/main.47712126.css.map,sha256=RdfmXfQvzvvh4XZuDWShm8CD8pA2Gb2K6MtMzrgJyKM,350495
18
18
  matlab_proxy/gui/static/js/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- matlab_proxy/gui/static/js/main.b7862305.js,sha256=M5s4SHw5A6PPjbwZZKWmlodnFxT1QVTP4OlsbgeEdQQ,294120
20
- matlab_proxy/gui/static/js/main.b7862305.js.LICENSE.txt,sha256=3cj3DrwM51esz1ogL9VVU1ZyXyXJ6u-Ec2CI9CCcI_A,1689
21
- matlab_proxy/gui/static/js/main.b7862305.js.map,sha256=OU_7Trg7CclLBHmHeAy3Xvc18-VIZelaatRQ6jB8Gbo,934275
19
+ matlab_proxy/gui/static/js/main.14aa7840.js,sha256=Eu6ZKinNe4gYoD1KV-CsYDJHg0DNKVq4-Qxy2sT2A-U,294128
20
+ matlab_proxy/gui/static/js/main.14aa7840.js.LICENSE.txt,sha256=3cj3DrwM51esz1ogL9VVU1ZyXyXJ6u-Ec2CI9CCcI_A,1689
21
+ matlab_proxy/gui/static/js/main.14aa7840.js.map,sha256=JbKOBbRaQIVoticAYynd6nEc1msGas1RXxnzvA1eWDs,935545
22
22
  matlab_proxy/gui/static/media/MATLAB-env-blur.4fc94edbc82d3184e5cb.png,sha256=QpmQTLDvBu2-b7ev83Rvpt0Q72R6wdQGkuJMPPpjv7M,220290
23
23
  matlab_proxy/gui/static/media/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  matlab_proxy/gui/static/media/arrow.0c2968b90bd9a64c8c3f.svg,sha256=XtmvDWzGZnwCZm08TKBnqt5hc1wphJnNupG0Fx_faAY,327
@@ -53,25 +53,25 @@ matlab_proxy/gui/static/media/trigger-error.3f1c4ef23ab8f3e60b0e.svg,sha256=3zzT
53
53
  matlab_proxy/gui/static/media/trigger-ok.7b9c238be42f685c4fa7.svg,sha256=mD-7N9cc4ARdMBFcplnogJv6nA4Yh3jQuYbZDUi18LU,4997
54
54
  matlab_proxy/icons/matlab.svg,sha256=xh5uYebQd8I-ISvenjU9A-PkClzW_lU9wvm3doXOFKM,13366
55
55
  matlab_proxy/matlab/startup.m,sha256=YRtI8P2flDJSDcPxJ2008B2l1T9JpqiUbzhQxA0frbc,1558
56
- matlab_proxy/util/__init__.py,sha256=7Qz_HwhNXDFMgO4DVQK1fwlc83TJ671mfzb3w6cLqRA,7714
56
+ matlab_proxy/util/__init__.py,sha256=bRfvYEbxsaiYhtWq5imTZM7PRrSEJ7GTlVBptvunaYQ,7902
57
57
  matlab_proxy/util/event_loop.py,sha256=Zqd282jlvPHHyc4kg8IjIzlzh9zLM_SAc5xjqUOrm04,1144
58
58
  matlab_proxy/util/list_servers.py,sha256=M93coVZjyQCdIvCCxsNOU_XDWNjBSysOJ5tWXaTjP8Y,1369
59
- matlab_proxy/util/mw.py,sha256=KCttG57vInsflRoHs1sEyUvQpd9faAs-ZiJE1rzfHDM,11048
59
+ matlab_proxy/util/mw.py,sha256=dLGSdfcTZiwKR1MMZA-39o-8na13IEPZOGBqcaHmKVI,11086
60
60
  matlab_proxy/util/system.py,sha256=XoT3Rv5MwPkdfhk2oMvUwxxlzZmADMlxzi9IRQyGgbA,1692
61
61
  matlab_proxy/util/windows.py,sha256=R9-VA7f0snVanSR7IgbHz3kvSnthZuUYe2UhBd5QWMQ,3440
62
62
  matlab_proxy/util/mwi/__init__.py,sha256=zI-X1lafr8H3j17PyA0oSZ0q5nINfK-WDA7VmJKmSAQ,158
63
63
  matlab_proxy/util/mwi/custom_http_headers.py,sha256=kfDjSnEXEVzoF2pZuEn76LKayeD2WKoQEDu2Y9EMOAo,7154
64
- matlab_proxy/util/mwi/environment_variables.py,sha256=BrG7FeXZ3nTE7BGFmItdNXRYpcfRMdIBaJCvj8llle0,6554
64
+ matlab_proxy/util/mwi/environment_variables.py,sha256=bIz0QFWo0pgyvSeJ_kAobUCS17V05CSmFWm3zYIOIsA,7139
65
65
  matlab_proxy/util/mwi/exceptions.py,sha256=93HrHbOq24KI4Md2el23XN01wIqVShEK29Pbt2V0Jr8,4628
66
66
  matlab_proxy/util/mwi/logger.py,sha256=e7wTPclrtJ-aX5mPk_pUJMX7-1QD_snGBW1P2ks-ETE,3311
67
- matlab_proxy/util/mwi/token_auth.py,sha256=2HiLmfvi_ejTJAEyV0_Unt-CK_osZTwfROp6yQ6SGfY,9844
68
- matlab_proxy/util/mwi/validators.py,sha256=Do0w8pjzCpYg9EiWqUtDojnBw8iwqkMR7K3ZQ8cBK8U,11745
67
+ matlab_proxy/util/mwi/token_auth.py,sha256=25uQE2cul6MZNTj1HQqukiBS014dj7URZfMOFPKcS3Y,9606
68
+ matlab_proxy/util/mwi/validators.py,sha256=BMMOD-0tdjGIALm1MmG4yXVRoD2ZUXkrJXLWP_D5FGo,11712
69
69
  matlab_proxy/util/mwi/embedded_connector/__init__.py,sha256=SVSckEJ4zQQ2KkNPT_un8ndMAImcMOTrai7ShAbmFY4,114
70
70
  matlab_proxy/util/mwi/embedded_connector/helpers.py,sha256=SfrwpCnZ6QGJBko-sR2bIG4-_qZOn29zCZV9qwVBnkM,3395
71
- matlab_proxy/util/mwi/embedded_connector/request.py,sha256=0NyfnE7-0g3BBPTPmjbkDrTOGiTNqFDrqKDObcRc2Jw,3716
72
- matlab_proxy-0.12.1.dist-info/LICENSE.md,sha256=oF0h3UdSF-rlUiMGYwi086ZHqelzz7yOOk9HFDv9ELo,2344
73
- matlab_proxy-0.12.1.dist-info/METADATA,sha256=_1rV3wq2j1sk_3BHdbdYvc8SFjXDsfGJC-A6YfHxB9Q,9909
74
- matlab_proxy-0.12.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
75
- matlab_proxy-0.12.1.dist-info/entry_points.txt,sha256=DbBLYgnRt8UGiOpd0zHigRTyyMdZYhMdvCvSYP7wPN0,244
76
- matlab_proxy-0.12.1.dist-info/top_level.txt,sha256=3-Lxje1LZvSfK__TVoRksU4th_PCj6cMMJwhH8P0_3I,13
77
- matlab_proxy-0.12.1.dist-info/RECORD,,
71
+ matlab_proxy/util/mwi/embedded_connector/request.py,sha256=EaQfV2R5lhzfnob0rZG2Eb9rl7BYd7_ZyK0jNIZzH6g,3730
72
+ matlab_proxy-0.14.0.dist-info/LICENSE.md,sha256=oF0h3UdSF-rlUiMGYwi086ZHqelzz7yOOk9HFDv9ELo,2344
73
+ matlab_proxy-0.14.0.dist-info/METADATA,sha256=R6iheeeUFTuovrhUK0SuSe1coTJK1-kJE925p7TrKhA,10108
74
+ matlab_proxy-0.14.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
75
+ matlab_proxy-0.14.0.dist-info/entry_points.txt,sha256=DbBLYgnRt8UGiOpd0zHigRTyyMdZYhMdvCvSYP7wPN0,244
76
+ matlab_proxy-0.14.0.dist-info/top_level.txt,sha256=3-Lxje1LZvSfK__TVoRksU4th_PCj6cMMJwhH8P0_3I,13
77
+ matlab_proxy-0.14.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5