Skip to content

Commit

Permalink
Introduces environment variable MWI_CUSTOM_MATLAB_ROOT to instruct …
Browse files Browse the repository at this point in the history
…`matlab-proxy` to use specified MATLAB instead of using the MATLAB found on the system PATH.

fixes #3
fixes mathworks/jupyter-matlab-proxy#37
  • Loading branch information
diningPhilosopher64 authored and Prabhakar Kumar committed Jul 11, 2023
1 parent 62d5bcf commit bc3cbb0
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 49 deletions.
43 changes: 42 additions & 1 deletion Advanced-Usage.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Advanced Usage
Copyright (c) 2020-2022 The MathWorks, Inc. All rights reserved.
Copyright (c) 2020-2023 The MathWorks, Inc. All rights reserved.

This page lists some of the advanced manuevers that may be of specific interest to help configure the package for use in your environment.

Expand Down Expand Up @@ -29,6 +29,47 @@ The following table describes all the environment variables that you can set to
| **MWI_ENABLE_TOKEN_AUTH** | string | `"True"` | When set to `True`, matlab-proxy will require users to provide the security token to access the proxy. <br />The default value is `False` . See [Token-Based Authentication](./SECURITY.md#token-based-authentication) for more information.|
| **MWI_AUTH_TOKEN** | string (optional) | `"AnyURLSafeToken"` | Optionally, provide a custom `token` for use with `MWI_ENABLE_TOKEN_AUTH`. A token can safely contain any combination of alpha numeric text along with the following permitted characters: `- . _ ~`.<br />When absent matlab-proxy will generate a random URL safe token. |
| **MWI_USE_EXISTING_LICENSE** | string (optional) | `"True"` | When set to True, matlab-proxy will not ask you for additional licensing information and will try to launch an already activated MATLAB on your system PATH.
| **MWI_CUSTOM_MATLAB_ROOT** | string (optional) | `"/path/to/matlab/root/"` | Optionally, provide a custom path to MATLAB root. For more information see [Adding MATLAB to System Path](#adding-matlab-to-system-path) |

## Adding MATLAB to System Path

When `matlab-proxy` starts, it expects the `matlab` executable to be present on system PATH in the environment from which it was spawned.

`matlab-proxy` will error out if it is unable to find `matlab` on the PATH.

One can add it to the system PATH using the following commands:
```bash
# On Linux & MacOS
sudo ln -fs ${MATLAB_ROOT}/bin/matlab /usr/bin/matlab

# On Windows environments
setx PATH "${MATLAB_ROOT}\bin;%PATH%"
```
Where `MATLAB_ROOT` points to the folder in which MATLAB was installed.
Example values of `MATLAB_ROOT` on various platforms are:
```
On linux: /usr/local/MATLAB/R2023a
On MacOS: /Applications/MATLAB_R2023a.app
On Windows: C:\Program Files\MATLAB\R2023a
```

### Custom MATLAB Root

Use the environment variable `MWI_CUSTOM_MATLAB_ROOT` to specify the location of `MATLAB_ROOT`.

When this environment variable is set, `matlab-proxy` will not search the system PATH for MATLAB.

This might be useful in the following situations:

1. Changes to the system PATH are not possible or desirable.
2. There are multiple MATLAB installations on a system, and you want to use `matlab-proxy` with a particular installation of MATLAB.
3. The existing `matlab` executable on PATH is a user defined script as explained in this [issue](https://github.com/mathworks/matlab-proxy/issues/3).

Example usage:
```bash
env MWI_CUSTOM_MATLAB_ROOT=/opt/software/matlab/r2023a matlab-proxy-app
```



## Custom HTTP Headers
Expand Down
4 changes: 2 additions & 2 deletions matlab_proxy/app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2022 The MathWorks, Inc.
# Copyright (c) 2020-2023 The MathWorks, Inc.

import asyncio
import json
Expand All @@ -19,7 +19,7 @@
from matlab_proxy.util import list_servers, mwi
from matlab_proxy.util.mwi import environment_variables as mwi_env
from matlab_proxy.util.mwi import token_auth
from matlab_proxy.util.mwi.exceptions import AppError, LicensingError, InvalidTokenError
from matlab_proxy.util.mwi.exceptions import AppError, InvalidTokenError, LicensingError

mimetypes.add_type("font/woff", ".woff")
mimetypes.add_type("font/woff2", ".woff2")
Expand Down
4 changes: 3 additions & 1 deletion matlab_proxy/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""This module defines project-level constants"""
# Copyright (c) 2023 The MathWorks, Inc.

"""This module defines project-level constants"""
CONNECTOR_SECUREPORT_FILENAME = "connector.securePort"
VERSION_INFO_FILE_NAME = "VersionInfo.xml"
59 changes: 50 additions & 9 deletions matlab_proxy/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,68 @@
from pathlib import Path

import matlab_proxy
from matlab_proxy.constants import VERSION_INFO_FILE_NAME
from matlab_proxy.util import mwi, system
from matlab_proxy.util.mwi import environment_variables as mwi_env
from matlab_proxy.util.mwi import token_auth

logger = mwi.logger.get()


def get_matlab_path():
def get_matlab_root_path():
"""Returns the path from the MWI_CUSTOM_MATLAB_ROOT environment variable if valid, else returns
MATLAB root based on the matlab executable if found on the system path.
Returns:
pathlib.Path: pathlib.Path object to MATLAB root.
"""
custom_matlab_root_path = os.environ.get(mwi_env.get_env_name_custom_matlab_root())

if custom_matlab_root_path and mwi.validators.validate_custom_matlab_root_path(
Path(custom_matlab_root_path)
):
return custom_matlab_root_path

which_matlab = shutil.which("matlab")
if which_matlab is None:

return Path(which_matlab).resolve().parent.parent if which_matlab else None


def get_matlab_executable_path(matlab_root_path: Path):
"""Returns path to the MATLAB executable based on the OS
Args:
matlab_root_path (Path): Path to MATLAB Root
Returns:
[Path | None]: Path to MATLAB executable if a valid MATLAB root path is supplied else return None
"""
if not matlab_root_path:
return None
return Path(which_matlab).resolve().parent.parent

return (
matlab_root_path / "bin" / "matlab"
if system.is_posix()
else matlab_root_path / "bin" / "matlab.exe"
)


def get_matlab_version(matlab_path):
"""Get the MATLAB Release version in this image"""
def get_matlab_version(matlab_root_path):
"""Returns MATLAB version from VersionInfo.xml file present at matlab_root_path
if matlab_path is None:
Args:
matlab_root_path (pathlib.Path): pathlib.Path to MATLAB root.
Returns:
(str | None): Returns MATLAB version from VersionInfo.xml file.
"""
if matlab_root_path is None:
return None

tree = ET.parse(matlab_path / "VersionInfo.xml")
version_info_file_path = Path(matlab_root_path) / VERSION_INFO_FILE_NAME
tree = ET.parse(version_info_file_path)
root = tree.getroot()

return root.find("release").text


Expand Down Expand Up @@ -137,7 +177,8 @@ def get(config_name=matlab_proxy.get_default_config_name(), dev=False):
matlab_startup_file = str(
Path(__file__).resolve().parent / "matlab" / "startup.m"
)
matlab_path = get_matlab_path()
matlab_path = get_matlab_root_path()
matlab_executable_path = get_matlab_executable_path(Path(matlab_path))
ws_env, ws_env_suffix = get_ws_env_settings()

ssl_key_file, ssl_cert_file = mwi.validators.validate_ssl_key_and_cert_file(
Expand Down Expand Up @@ -172,7 +213,7 @@ def get(config_name=matlab_proxy.get_default_config_name(), dev=False):
"matlab_path": matlab_path,
"matlab_version": get_matlab_version(matlab_path),
"matlab_cmd": [
"matlab",
matlab_executable_path,
"-nosplash",
flag_to_hide_desktop,
"-softwareopengl",
Expand Down
18 changes: 16 additions & 2 deletions matlab_proxy/util/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright (c) 2020-2022 The MathWorks, Inc.
# Copyright (c) 2020-2023 The MathWorks, Inc.
import argparse
import os
import socket
import sys

from pathlib import Path

import matlab_proxy
from matlab_proxy.util import mwi, system
Expand Down Expand Up @@ -241,3 +242,16 @@ def get_access_url(app):
url = f"{access_protocol}://{host_interface}:{port}{base_url}"

return url


def is_valid_path(path: Path):
"""Returns true if path supplied is a valid path to a file or directory
Args:
path (pathlib.Path): pathlib.Path object of a file or directory
Returns:
bool: True if a valid path is supplied else False
"""
path = Path(path)
return path.is_dir() or path.is_file()
7 changes: 6 additions & 1 deletion matlab_proxy/util/mwi/environment_variables.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2022 The MathWorks, Inc.
# Copyright (c) 2020-2023 The MathWorks, Inc.
"""This file lists and exposes the environment variables which are used by the integration."""

import os
Expand Down Expand Up @@ -145,3 +145,8 @@ def get_env_name_matlab_log_dir():
def get_env_name_mwi_use_existing_license():
"""Returns the environment variable name used to instruct matlab-proxy to use an existing license. Usually used by already activated MATLAB installations."""
return "MWI_USE_EXISTING_LICENSE"


def get_env_name_custom_matlab_root():
"""User specified path to MATLAB root"""
return "MWI_CUSTOM_MATLAB_ROOT"
58 changes: 57 additions & 1 deletion matlab_proxy/util/mwi/validators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2022 The MathWorks, Inc.
# Copyright (c) 2020-2023 The MathWorks, Inc.
"""This file contains validators for various runtime artefacts.
A validator is defined as a function which verifies the input and
returns it unchanged if validation passes.
Expand All @@ -14,15 +14,23 @@
import os
import socket
import sys
from pathlib import Path

from typing import List

import pkg_resources

import matlab_proxy
from matlab_proxy.constants import VERSION_INFO_FILE_NAME

from . import environment_variables as mwi_env
from matlab_proxy.util import system
from . import logger as mwi_logger

from matlab_proxy import util

from .exceptions import MatlabError

logger = mwi_logger.get()


Expand Down Expand Up @@ -276,3 +284,51 @@ def validate_use_existing_licensing(use_existing_license):
bool: if use_existing_license is set to true
"""
return True if use_existing_license.casefold() == "true" else False


def validate_paths(paths: List[Path]):
"""Validates if paths of directories or files exists on the file system.
Args:
paths ([pathlib.Path]): List of pathlib.Path's to directories or files
Raises:
OSError: When an invalid path is supplied
Returns:
[pathlib.Path] | None: [pathlib.Path] if valid paths are supplied else None
"""
for path in paths:
if not util.is_valid_path(path):
raise OSError(f"Supplied invalid path:{path}")

return paths


def validate_custom_matlab_root_path(matlab_root: Path):
"""Validate if path supplied is MATLAB_ROOT by checking for the existence of VersionInfo.xml file
at matlab_root
Args:
path (pathlib.Path): path to MATLAB root
Returns:
pathlib.Path | None: pathlib.Path if a valid path to MATLAB root is supplied else None
"""
try:
matlab_root = matlab_root
validate_paths([matlab_root])
logger.debug(f"Supplied valid MATLAB root path:{matlab_root}")
except OSError as exc:
logger.error(". ".join(exc.args))
sys.exit(1)

version_info_file_path = matlab_root / VERSION_INFO_FILE_NAME

if not version_info_file_path.is_file():
logger.error(
f" {VERSION_INFO_FILE_NAME} file doesn't exist at the provided path :{matlab_root}"
)
sys.exit(1)

return matlab_root
Loading

0 comments on commit bc3cbb0

Please sign in to comment.