From 3fa7c1e7ad9eb7d3aa72d3e6942ddffd287ea201 Mon Sep 17 00:00:00 2001 From: Krishan Sharma Date: Thu, 11 Dec 2025 13:07:53 +0000 Subject: [PATCH 1/2] Adds support for notebook magic `%matlab [OPTIONS]`. Enables notebooks to specify the use of a dedicated MATLAB. For more information, see (Technical Overview of MATLAB Kernel for Jupyter)[https://github.com/mathworks/jupyter-matlab-proxy/tree/main/src/jupyter_matlab_kernel#technical-overview] fixes mathworks/jupyter-matlab-proxy#103 fixes mathworks/jupyter-matlab-proxy#44 --- README.md | 4 +- img/kernel-architecture-dedicated.png | Bin 0 -> 183123 bytes img/kernel-architecture.png | Bin 33567 -> 0 bytes src/jupyter_matlab_kernel/README.md | 21 +- src/jupyter_matlab_kernel/base_kernel.py | 225 +++++++++++----- src/jupyter_matlab_kernel/jsp_kernel.py | 25 +- .../magic_execution_engine.py | 2 +- src/jupyter_matlab_kernel/magics/README.md | 4 +- .../magics/base/matlab_magic.py | 7 +- src/jupyter_matlab_kernel/magics/matlab.py | 219 ++++++++++++++++ src/jupyter_matlab_kernel/mpm_kernel.py | 60 +---- src/jupyter_matlab_kernel/mwi_comm_helpers.py | 104 ++++++-- src/jupyter_matlab_kernel/mwi_exceptions.py | 4 +- .../src/plugins/matlabToolbarButton.ts | 93 +++++-- .../src/tests/matlabToolbarButton.test.ts | 244 +++++++++++------- .../style/base.css | 6 + .../utils/integration_test_utils.py | 35 ++- .../magics/test_matlab.py | 202 +++++++++++++++ .../unit/jupyter_matlab_kernel/test_kernel.py | 23 +- .../jupyter_matlab_kernel/test_mpm_kernel.py | 25 +- .../test_mwi_comm_helpers.py | 108 ++++---- 21 files changed, 1074 insertions(+), 337 deletions(-) create mode 100644 img/kernel-architecture-dedicated.png delete mode 100644 img/kernel-architecture.png create mode 100644 src/jupyter_matlab_kernel/magics/matlab.py create mode 100644 tests/unit/jupyter_matlab_kernel/magics/test_matlab.py diff --git a/README.md b/README.md index 1769840d..d4432d6e 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,8 @@ This opens a Jupyter notebook that supports MATLAB. - **Licensing:** When you execute MATLAB code in a notebook for the first time, enter your MATLAB license information in the dialog box that appears. For details, see [Licensing](https://github.com/mathworks/matlab-proxy/blob/main/MATLAB-Licensing-Info.md). The MATLAB session can take a few minutes to start. +- **Sharing MATLAB across notebooks:** By default, multiple notebooks running on a Jupyter server share the underlying MATLAB process, so executing code in one notebook affects the workspace in others. To use a dedicated MATLAB for your kernel instead, use the magic `%%matlab new_session`. For details, see [Magic Commands for MATLAB Kernel](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/magics/README.md). To learn more about the kernel architecture, see [MATLAB Kernel for Jupyter](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/README.md). + - **MATLAB Kernel:** The MATLAB kernel supports tab completion and rich outputs: * Inline static plot images * LaTeX representation for symbolic expressions @@ -181,7 +183,7 @@ This opens a Jupyter notebook that supports MATLAB. For a technical overview of the MATLAB kernel, see [MATLAB Kernel for Jupyter](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/README.md). -- **Multiple notebooks:** Multiple notebooks running on a Jupyter server share the underlying MATLAB process, so executing code in one notebook affects the workspace in others. If you work in several notebooks simultaneously, be aware they share a workspace. For details, see [MATLAB Kernel for Jupyter](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/README.md). + - **Local functions:** With MATLAB R2022b and later, you can define a local function at the end of the cell where you want to call it:

diff --git a/img/kernel-architecture-dedicated.png b/img/kernel-architecture-dedicated.png new file mode 100644 index 0000000000000000000000000000000000000000..cb9a1fcb4b711ea3f387fb6ed418915e8f03f1e1 GIT binary patch literal 183123 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

Xw- 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 literal 0 HcmV?d00001 diff --git a/img/kernel-architecture.png b/img/kernel-architecture.png deleted file mode 100644 index 367fb79622f0b9b8e2bcdf53a412c602e0ce83a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33567 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

| -|--| -|The diagram above illustrates that multiple Jupyter notebooks communicate with a shared MATLAB process, through the Jupyter notebook server.| +

-Start a Jupyter notebook to create a MATLAB kernel. When you run MATLAB code in a notebook for the first time, you see a licensing screen to enter your MATLAB license details. If a MATLAB process is not already running, Jupyter will start one. +### Shared MATLAB Workspace (Default Behavior) -Multiple notebooks share the same MATLAB workspace. MATLAB processes commands from multiple notebooks in on a first-in, first-out basis. +By default, multiple notebooks share the same MATLAB workspace. MATLAB processes commands from multiple notebooks on a first-in, first-out basis. -You can use kernel interrupts to stop MATLAB from processing a request. Remember that if cells from multiple notebooks are being run at the same time, the execution request you interrupt may not be from the notebook where you initated the interrupt. +You can use kernel interrupts to stop MATLAB from processing a request. Remember that if cells from multiple notebooks are being run at the same time, the execution request you interrupt may not be from the notebook where you initiated the interrupt. + +### Dedicated MATLAB Workspace (Optional Behavior) + +You can now create a dedicated MATLAB session for your notebook by using the magic command `%%matlab new_session` in a cell. This starts a separate MATLAB process exclusively for that notebook, providing an isolated workspace that is not shared with other notebooks. + +This is useful when you need to avoid conflicts with other notebooks or require an independent execution environment. + +Once created, all subsequent MATLAB code in that notebook will execute in the dedicated session. Each dedicated session operates independently with its own workspace and execution queue. ## Limitations @@ -33,6 +40,6 @@ To request an enhancement or technical support, [create a GitHub issue](https:// ---- -Copyright 2023-2024 The MathWorks, Inc. +Copyright 2023-2025 The MathWorks, Inc. ---- diff --git a/src/jupyter_matlab_kernel/base_kernel.py b/src/jupyter_matlab_kernel/base_kernel.py index ec0369e0..253a48ed 100644 --- a/src/jupyter_matlab_kernel/base_kernel.py +++ b/src/jupyter_matlab_kernel/base_kernel.py @@ -25,6 +25,7 @@ MagicExecutionEngine, get_completion_result_for_magics, ) +from jupyter_matlab_kernel.mwi_comm_helpers import MWICommHelper from jupyter_matlab_kernel.mwi_exceptions import MATLABConnectionError from jupyter_matlab_kernel.comms import LabExtensionCommunication @@ -141,7 +142,22 @@ def __init__(self, *args, **kwargs): self.magic_engine = MagicExecutionEngine(self.log) # Communication helper for interaction with backend MATLAB proxy - self.mwi_comm_helper = None + self.mwi_comm_helper: Optional[MWICommHelper] = None + + # Used to detect if this Kernel has been assigned a MATLAB-proxy server or not + self.is_matlab_assigned = False + + # Flag indicating whether this kernel is using a shared MATLAB instance + self.is_shared_matlab: bool = True + + # Keeps track of MATLAB version information for the MATLAB assigned to this Kernel + self.matlab_version = None + + # Keeps track of MATLAB root path information for the MATLAB assigned to this Kernel + self.matlab_root_path = None + + # Keeps track of the MATLAB licensing mode information for the MATLAB assigned to this Kernel + self.licensing_mode = None self.labext_comm = LabExtensionCommunication(self) @@ -165,8 +181,9 @@ async def interrupt_request(self, stream, ident, parent): """ self.log.debug("Received interrupt request from Jupyter") try: - # Send interrupt request to MATLAB - await self.mwi_comm_helper.send_interrupt_request_to_matlab() + if self.is_matlab_assigned and self.mwi_comm_helper: + # Send interrupt request to MATLAB + await self.mwi_comm_helper.send_interrupt_request_to_matlab() # Set the response to interrupt request. content = {"status": "ok"} @@ -184,29 +201,6 @@ async def interrupt_request(self, stream, ident, parent): self.session.send(stream, "interrupt_reply", content, parent, ident=ident) - def modify_kernel(self, states_to_modify): - """ - Used to modify MATLAB Kernel state - Args: - states_to_modify (dict): A key value pair of all the states to be modified. - - """ - self.log.debug(f"Modifying the kernel with {states_to_modify}") - for key, value in states_to_modify.items(): - if hasattr(self, key): - self.log.debug(f"set the value of {key} to {value}") - setattr(self, key, value) - - def handle_magic_output(self, output, outputs=None): - if output["type"] == "modify_kernel": - self.modify_kernel(output) - else: - self.display_output(output) - if outputs is not None and not self.startup_checks_completed: - # Outputs are cleared after startup_check. - # Storing the magic outputs to display them after startup_check completes. - outputs.append(output) - async def do_execute( self, code, @@ -222,18 +216,19 @@ async def do_execute( https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute """ self.log.debug(f"Received execution request from Jupyter with code:\n{code}") + try: - accumulated_magic_outputs = [] performed_startup_checks = False - - for output in self.magic_engine.process_before_cell_execution( - code, self.execution_count - ): - self.handle_magic_output(output, accumulated_magic_outputs) + accumulated_magic_outputs = await self._perform_before_cell_execution(code) skip_cell_execution = self.magic_engine.skip_cell_execution() self.log.debug(f"Skipping cell execution is set to {skip_cell_execution}") + # Start a shared matlab-proxy (default) if not already started + if not self.is_matlab_assigned and not skip_cell_execution: + await self.start_matlab_proxy_and_comm_helper() + self.is_matlab_assigned = True + # Complete one-time startup checks before sending request to MATLAB. # Blocking call, returns after MATLAB is started. if not skip_cell_execution: @@ -275,9 +270,8 @@ async def do_execute( ) # Display all the outputs produced during the execution of code. - for idx in range(len(outputs)): - data = outputs[idx] - self.log.debug(f"Displaying output {idx+1}:\n{data}") + for idx, data in enumerate(outputs): + self.log.debug(f"Displaying output {idx + 1}:\n{data}") # Ignore empty values returned from MATLAB. if not data: @@ -286,7 +280,7 @@ async def do_execute( # Execute post execution of MAGICs for output in self.magic_engine.process_after_cell_execution(): - self.handle_magic_output(output) + await self._handle_magic_output(output) except Exception as e: self.log.error( @@ -418,6 +412,119 @@ async def do_history( # Helper functions + def _get_kernel_info(self): + return { + "is_shared_matlab": self.is_shared_matlab, + "matlab_version": self.matlab_version, + "matlab_root_path": self.matlab_root_path, + "licensing_mode": self.licensing_mode, + } + + def _modify_kernel(self, states_to_modify): + """ + Used to modify MATLAB Kernel state + Args: + states_to_modify (dict): A key value pair of all the states to be modified. + + """ + self.log.info(f"Modifying the kernel with {states_to_modify}") + for key, value in states_to_modify.items(): + if hasattr(self, key): + self.log.debug(f"set the value of {key} to {value}") + setattr(self, key, value) + else: + self.log.warning(f"Attribute with name: {key} not found in kernel") + + async def _handle_magic_output(self, output): + """ + Handle the output from magic commands. + + Args: + output (dict): The output from a magic command. + + Returns: + dict or None: Returns the output if startup checks are not completed, + otherwise returns None. + + This method processes the output from magic commands. It handles kernel + modifications, stores outputs before startup checks are completed, and + displays outputs after startup checks are done. + """ + if output["type"] == "modify_kernel": + self.log.debug("Handling modify_kernel output") + self._modify_kernel(output) + elif output["type"] == "callback": + self.log.debug("Handling callback output") + await self._invoke_callback_function(output.get("callback_function")) + else: + self.display_output(output) + + if not self.startup_checks_completed: + # Outputs are cleared after startup_check. + # Storing the magic outputs to display them after startup_check completes. + return output + return None + + async def _invoke_callback_function(self, callback_fx): + """ + Handles the invocation of callback function supplied by the magic command. Kernel injects + itself as a parameter. + + Args: + callback_fx: Function to be called. Currently only supports calling async or async generator functions. + """ + if callback_fx: + import inspect + + if inspect.isasyncgenfunction(callback_fx): + async for result in callback_fx(self): + self.display_output(result) + else: + result = await callback_fx(self) + if result: + self.display_output(result) + self.log.debug(f"Callback function {callback_fx} executed successfully") + return None + + async def start_matlab_proxy_and_comm_helper(self): + """ + Start MATLAB proxy and communication helper. + + This method is intended to be overridden by subclasses to perform + any necessary setup for matlab-proxy startup. The default implementation + does nothing. + + Returns: + None + + Raises: + NotImplementedError: Always raised as this method must be implemented by subclasses. + """ + raise NotImplementedError("Subclasses should implement this method") + + async def _perform_before_cell_execution(self, code) -> list: + """ + Perform actions before cell execution and handle magic outputs. + + This method processes magic commands before cell execution and accumulates + their outputs. + + Args: + code (str): The code to be executed. + + Returns: + list: A list of accumulated magic outputs. + """ + accumulated_magic_outputs = [] + for magic_output in self.magic_engine.process_before_cell_execution( + code, self.execution_count + ): + output = await self._handle_magic_output(magic_output) + if output: + accumulated_magic_outputs.append(output) + + return accumulated_magic_outputs + def display_output(self, out): """ Common function to send execution outputs to Jupyter UI. @@ -472,11 +579,8 @@ async def perform_startup_checks( self.log.error(f"Found a startup error: {self.startup_error}") raise self.startup_error - ( - is_matlab_licensed, - matlab_status, - matlab_proxy_has_error, - ) = await self.mwi_comm_helper.fetch_matlab_proxy_status() + # Query matlab-proxy for its current status + matlab_proxy_status = await self.mwi_comm_helper.fetch_matlab_proxy_status() # Display iframe containing matlab-proxy to show login window if MATLAB # is not licensed using matlab-proxy. The iframe is removed after MATLAB @@ -486,7 +590,7 @@ async def perform_startup_checks( # as src for iframe to avoid hardcoding any hostname/domain information. This is done to # ensure the kernel works in Jupyter deployments. VS Code however does not work the same way # as other browser based Jupyter clients. - if not is_matlab_licensed: + if not matlab_proxy_status.is_matlab_licensed: if not jupyter_base_url: # happens for non-jupyter environments (like VSCode), we expect licensing to # be completed before hand @@ -519,22 +623,13 @@ async def perform_startup_checks( ) # Wait until MATLAB is started before sending requests. - await self.poll_for_matlab_startup( - is_matlab_licensed, matlab_status, matlab_proxy_has_error - ) + await self.poll_for_matlab_startup(matlab_proxy_status) - async def poll_for_matlab_startup( - self, is_matlab_licensed, matlab_status, matlab_proxy_has_error - ): - """Wait until MATLAB has started or time has run out" + async def poll_for_matlab_startup(self, matlab_proxy_status): + """Wait until MATLAB has started or time has run out Args: - is_matlab_licensed (bool): A flag indicating whether MATLAB is - licensed and eligible to start. - matlab_status (str): A string representing the current status - of the MATLAB startup process. - matlab_proxy_has_error (bool): A flag indicating whether there - is an error in the MATLAB proxy process during startup. + matlab_proxy_status: The status object from matlab-proxy Raises: MATLABConnectionError: If an error occurs while attempting to @@ -544,11 +639,12 @@ async def poll_for_matlab_startup( self.log.debug("Waiting until MATLAB is started") timeout = 0 while ( - matlab_status != "up" + matlab_proxy_status + and matlab_proxy_status.matlab_status != "up" and timeout != _MATLAB_STARTUP_TIMEOUT - and not matlab_proxy_has_error + and not matlab_proxy_status.matlab_proxy_has_error ): - if is_matlab_licensed: + if matlab_proxy_status.is_matlab_licensed: if timeout == 0: self.log.debug("Licensing completed. Clearing output area") self.display_output( @@ -565,11 +661,7 @@ async def poll_for_matlab_startup( ) timeout += 1 time.sleep(1) - ( - is_matlab_licensed, - matlab_status, - matlab_proxy_has_error, - ) = await self.mwi_comm_helper.fetch_matlab_proxy_status() + matlab_proxy_status = await self.mwi_comm_helper.fetch_matlab_proxy_status() # If MATLAB is not available after 15 seconds of licensing information # being available either through user input or through matlab-proxy cache, @@ -580,10 +672,15 @@ async def poll_for_matlab_startup( ) raise MATLABConnectionError - if matlab_proxy_has_error: + if not matlab_proxy_status or matlab_proxy_status.matlab_proxy_has_error: self.log.error("matlab-proxy encountered error.") raise MATLABConnectionError + # Update the kernel state with information from matlab proxy server + self.licensing_mode = matlab_proxy_status.licensing_mode + self.matlab_version = matlab_proxy_status.matlab_version + self.matlab_root_path = await self.mwi_comm_helper.fetch_matlab_root_path() + self.log.debug("MATLAB is running, startup checks completed.") def _extract_kernel_id_from_sys_args(self, args) -> str: diff --git a/src/jupyter_matlab_kernel/jsp_kernel.py b/src/jupyter_matlab_kernel/jsp_kernel.py index dd0c4793..8efbc73a 100644 --- a/src/jupyter_matlab_kernel/jsp_kernel.py +++ b/src/jupyter_matlab_kernel/jsp_kernel.py @@ -186,19 +186,24 @@ def __init__(self, *args, **kwargs): async def do_shutdown(self, restart): self.log.debug("Received shutdown request from Jupyter") - try: - await self.mwi_comm_helper.send_shutdown_request_to_matlab() - await self.mwi_comm_helper.disconnect() - except ( - MATLABConnectionError, - aiohttp.client_exceptions.ClientResponseError, - ) as e: - self.log.error( - f"Exception occurred while sending shutdown request to MATLAB:\n{e}" - ) + if self.is_matlab_assigned: + try: + await self.mwi_comm_helper.send_shutdown_request_to_matlab() + await self.mwi_comm_helper.disconnect() + except ( + MATLABConnectionError, + aiohttp.client_exceptions.ClientResponseError, + ) as e: + self.log.error( + f"Exception occurred while sending shutdown request to MATLAB:\n{e}" + ) return super().do_shutdown(restart) async def perform_startup_checks(self): """Overriding base function to provide a different iframe source""" await super().perform_startup_checks(self.jupyter_base_url, "matlab") + + async def start_matlab_proxy_and_comm_helper(self): + """Default implementation assumes that matlab is assigned""" + self.is_matlab_assigned = True diff --git a/src/jupyter_matlab_kernel/magic_execution_engine.py b/src/jupyter_matlab_kernel/magic_execution_engine.py index 58811a2f..9169aadf 100644 --- a/src/jupyter_matlab_kernel/magic_execution_engine.py +++ b/src/jupyter_matlab_kernel/magic_execution_engine.py @@ -181,7 +181,7 @@ def magic_executor(magics_for_execution, magic_execution_function): for output_from_method in magic_method(): if output_from_method: if "type" in output_from_method: - yield output_from_method + yield (output_from_method) else: raise MagicError( f"Invalid result returned by a Magic command. Contact Magic Author to fix. \n Error: {output_from_method}\n Does not contain a key called type." diff --git a/src/jupyter_matlab_kernel/magics/README.md b/src/jupyter_matlab_kernel/magics/README.md index 117bfe43..038f8f8d 100644 --- a/src/jupyter_matlab_kernel/magics/README.md +++ b/src/jupyter_matlab_kernel/magics/README.md @@ -15,6 +15,8 @@ This table lists the predefined magic commands you can use: |---|---|---|---|---| |`?` and `help`| Display documentation of given magic command.|Name of magic command.||`%%lsmagic?` or `%%help lsmagic`| |`lsmagic`|List predefined magic commands.|||`%%lsmagic`| +|`matlab new_session`|Starts a new MATLAB dedicated to the kernel instead of being shared across kernels.

Note: To change from a shared MATLAB to a dedicated MATLAB after you have already run MATLAB code in a notebook, you must first restart the kernel.|||`%%matlab new_session`| +|`matlab info`|Print a summary of the MATLAB session currently being used for the kernel. The summary includes the MATLAB version, root path, licensing mode, and whether the MATLAB is shared or dedicated to a kernel. |||`%%matlab info`| |`time`|Display time taken to execute a cell.|||`%%time`| |`file`|Save contents of cell as a file in the notebook folder. You can use this command to define and save new functions. For details, see the section below on how to [Create New Functions Using the %%file Magic Command](#create-new-functions-using-the-the-file-magic-command)|Name of saved file.|The file magic command will save the contents of the cell, but not execute them in MATLAB.|`%%file myfile.m`| @@ -63,6 +65,6 @@ Note: to use your function in MATLAB, remember to add the Jupyter notebook folde --- -Copyright 2024 The MathWorks, Inc. +Copyright 2024-2025 The MathWorks, Inc. --- diff --git a/src/jupyter_matlab_kernel/magics/base/matlab_magic.py b/src/jupyter_matlab_kernel/magics/base/matlab_magic.py index 51031089..8015399f 100644 --- a/src/jupyter_matlab_kernel/magics/base/matlab_magic.py +++ b/src/jupyter_matlab_kernel/magics/base/matlab_magic.py @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. from jupyter_matlab_kernel import mwi_logger @@ -83,6 +83,11 @@ def before_cell_execute(self): "murl": new_url, "headers": new_headers, } + 4. To invoke the callback function: + { + "type": "callback", + "callback_function": callback_function_name, + } default: Empty dict ({}). """ yield {} diff --git a/src/jupyter_matlab_kernel/magics/matlab.py b/src/jupyter_matlab_kernel/magics/matlab.py new file mode 100644 index 00000000..5a207cbf --- /dev/null +++ b/src/jupyter_matlab_kernel/magics/matlab.py @@ -0,0 +1,219 @@ +# Copyright 2025 The MathWorks, Inc. + +from jupyter_matlab_kernel.magics.base.matlab_magic import MATLABMagic +from jupyter_matlab_kernel.mpm_kernel import MATLABKernelUsingMPM +from jupyter_matlab_kernel.mwi_exceptions import MagicError + +# Module constants +LICENSING_MODES = { + "mhlm": "Online Licensing", + "nlm": "Network License Manager", + "existing_license": "Existing License", +} +CMD_NEW_SESSION = "new_session" +CMD_INFO = "info" +EXISTING_NEW_SESSION_ERROR = "This kernel is already using a dedicated MATLAB.\n" +DEDICATED_SESSION_CONFIRMATION_MSG = ( + "A dedicated MATLAB session has been started for this kernel.\n" +) + + +async def handle_new_matlab_session(kernel: MATLABKernelUsingMPM): + """ + Handles the creation of a new dedicated MATLAB session for the kernel. + Args: + kernel: The kernel instance. + + Yields: + dict: Result dictionary containing execution status and confirmation message. + """ + # Validations + if kernel.is_matlab_assigned: + if not kernel.is_shared_matlab: + # No-op if already in an isolated MATLAB session + yield { + "type": "execute_result", + "mimetype": ["text/plain", "text/html"], + "value": [ + EXISTING_NEW_SESSION_ERROR, + f"
{EXISTING_NEW_SESSION_ERROR}
", + ], + } + return + + else: + # Shared MATLAB session is already assigned + kernel.log.warning( + "Cannot start a new MATLAB session while an existing session is active." + ) + raise MagicError( + "This notebook is currently linked to Default MATLAB session." + "To proceed, restart the kernel and run this magic command before any other MATLAB commands." + ) + # Starting new dedicated MATLAB session + try: + kernel.is_shared_matlab = False + await kernel.start_matlab_proxy_and_comm_helper() + kernel.is_matlab_assigned = True + + # Raises MATLABConnectionError if matlab-proxy failed to start in previous step + await kernel.perform_startup_checks() + kernel.startup_checks_completed = True + kernel.display_output({"type": "clear_output", "content": {"wait": False}}) + except Exception as ex: + _reset_kernel_state(kernel) + + # Try and cleanup the matlab-proxy process if it was started + await kernel.cleanup_matlab_proxy() + + # Raising here so that matlab magic output can display the error + raise MagicError(str(ex)) from ex + + yield { + "type": "execute_result", + "mimetype": ["text/plain", "text/html"], + "value": [ + DEDICATED_SESSION_CONFIRMATION_MSG, + f"
{DEDICATED_SESSION_CONFIRMATION_MSG}
", + ], + } + + +def _reset_kernel_state(kernel: MATLABKernelUsingMPM): + """ + Resets the kernel to its initial state for MATLAB session management. + + Args: + kernel (MATLABKernelUsingMPM): The MATLAB kernel instance whose state + needs to be reset. + """ + kernel.is_shared_matlab = True + kernel.is_matlab_assigned = False + kernel.startup_checks_completed = False + + +async def get_kernel_info(kernel): + """ + Provides information about the current MATLAB kernel state related to MATLAB. + + :param kernel: kernel object containing MATLAB information + """ + output = _format_info(kernel._get_kernel_info()) + yield { + "type": "execute_result", + "mimetype": ["text/plain", "text/html"], + "value": [ + output, + f"
{output}
", + ], + } + + +def _format_info(info) -> str: + """ + Formats MATLAB information into a formatted string. + + Args: + info: Dictionary containing MATLAB information. + + Returns: + str: Formatted string with MATLAB information. + """ + info_text = f'MATLAB Version: {info.get("matlab_version")}\n' + info_text += f'MATLAB Root Path: {info.get("matlab_root_path")}\n' + info_text += f'Licensing Mode: {LICENSING_MODES.get(info.get("licensing_mode"), "Unknown")}\n' + info_text += f'MATLAB Shared With Other Notebooks: {info.get("is_shared_matlab")}\n' + return info_text + + +class matlab(MATLABMagic): + info_about_magic = f""" + Starts a new MATLAB that is dedicated to the current kernel, instead of being shared across kernels. + + Usage: %%matlab {CMD_NEW_SESSION} or %%matlab {CMD_INFO} + + Note: To change from a shared MATLAB to a dedicated MATLAB after you have already run MATLAB code in a notebook, you must first restart the kernel. + """ + skip_matlab_execution = False + + def before_cell_execute(self): + """ + Processes the MATLAB magic command before cell execution. + + This method validates the parameters passed to the MATLAB magic command, + and yields appropriate callbacks based on the command type. + + Raises: + MagicError: If the number of parameters is not exactly one or if an unknown argument is provided. + + Yields: + dict: A dictionary containing callback information for the kernel to process. + The dictionary must contain a key called "type". Kernel injects itself into the callback function while + making the call. This ensures Kernel object is available to magic instance. + + Examples: To start a new matlab session or to display information about assigned MATLAB: + { + "type": "callback", + "callback_function": "function local to this module to be called from kernel", + } + """ + if len(self.parameters) != 1: + raise MagicError( + f"matlab magic expects only one argument. Received: {self.parameters}. Choose one of: {[arg for arg in self.get_supported_arguments()]}" + ) + + command = self.parameters[0] + # Handles "new_session" argument + if command == CMD_NEW_SESSION: + yield { + "type": "callback", + "callback_function": handle_new_matlab_session, + } + + # Handles "info" argument + elif command == CMD_INFO: + yield { + "type": "callback", + "callback_function": get_kernel_info, + } + + # Handles unknown arguments + else: + raise MagicError( + f"Unknown argument {command}. Choose one of: {[arg for arg in self.get_supported_arguments()]}" + ) + + def do_complete(self, parameters, parameter_pos, cursor_pos): + """ + Provides autocompletion for the matlab magic command. + + Args: + parameters (list): The parameters passed to the magic command + parameter_pos (int): The position of the parameter being completed + cursor_pos (int): The cursor position within the parameter + + Returns: + list: A list of possible completions + """ + matches = [] + if parameter_pos == 1: + # Show all the arguments under matlab magic + if cursor_pos == 0: + matches = self.get_supported_arguments() + # For partial input, match arguments that start with the current input + else: + matches = [ + s + for s in self.get_supported_arguments() + if s.startswith(parameters[0][:cursor_pos]) + ] + return matches + + def get_supported_arguments(self) -> list: + """ + Returns a list of supported arguments for the MATLAB magic command. + + Returns: + list: A list of supported arguments + """ + return [CMD_NEW_SESSION, CMD_INFO] diff --git a/src/jupyter_matlab_kernel/mpm_kernel.py b/src/jupyter_matlab_kernel/mpm_kernel.py index 1e2a9604..e52140a8 100644 --- a/src/jupyter_matlab_kernel/mpm_kernel.py +++ b/src/jupyter_matlab_kernel/mpm_kernel.py @@ -18,9 +18,6 @@ class MATLABKernelUsingMPM(base.BaseMATLABKernel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - # Used to detect if this Kernel has been assigned a MATLAB-proxy server or not - self.is_matlab_assigned = False - # Serves as the auth token to secure communication between Jupyter Server and MATLAB proxy manager self.mpm_auth_token = None @@ -35,41 +32,9 @@ def __init__(self, *args, **kwargs): # ipykernel Interface API # https://ipython.readthedocs.io/en/stable/development/wrapperkernels.html - async def do_execute( - self, - code, - silent, - store_history=True, - user_expressions=None, - allow_stdin=False, - *, - cell_id=None, - ): - """ - Used by ipykernel infrastructure for execution. For more info, look at - https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute - """ - self.log.debug(f"Received execution request from Jupyter with code:\n{code}") - - # Starts the matlab proxy process if this kernel hasn't yet been assigned a - # matlab proxy and sets the attributes on kernel to talk to the correct backend. - if not self.is_matlab_assigned: - self.log.debug("Starting matlab-proxy") - await self._start_matlab_proxy_and_comm_helper() - self.is_matlab_assigned = True - - return await super().do_execute( - code=code, - silent=silent, - store_history=store_history, - user_expressions=user_expressions, - allow_stdin=allow_stdin, - cell_id=cell_id, - ) - async def do_shutdown(self, restart): self.log.debug("Received shutdown request from Jupyter") - if self.is_matlab_assigned: + if self.is_matlab_assigned and self.mwi_comm_helper: try: # Cleans up internal live editor state, client session await self.mwi_comm_helper.send_shutdown_request_to_matlab() @@ -80,25 +45,28 @@ async def do_shutdown(self, restart): f"Exception occurred while sending shutdown request to MATLAB:\n{e}" ) except Exception as e: - self.log.debug("Exception during shutdown", e) + self.log.debug("Exception during shutdown: %s", e) finally: - # Shuts down matlab assigned to this Kernel (based on satisfying certain criteria) - await mpm_lib.shutdown( - self.parent_pid, self.kernel_id, self.mpm_auth_token - ) - self.is_matlab_assigned = False + await self.cleanup_matlab_proxy() return super().do_shutdown(restart) + # Helper functions + + async def cleanup_matlab_proxy(self): + # Shuts down matlab-proxy and MATLAB assigned to this Kernel. + # matlab-proxy process is cleaned up when this Kernel process is the + # only reference to the assigned matlab-proxy instance + await mpm_lib.shutdown(self.parent_pid, self.kernel_id, self.mpm_auth_token) + self.is_matlab_assigned = False + async def perform_startup_checks(self): """Overriding base function to provide a different iframe source""" await super().perform_startup_checks( self.jupyter_base_url, f"{self.matlab_proxy_base_url}/" ) - # Helper functions - - async def _start_matlab_proxy_and_comm_helper(self) -> None: + async def start_matlab_proxy_and_comm_helper(self) -> None: """ Starts the MATLAB proxy using the proxy manager and fetches its status. """ @@ -137,7 +105,7 @@ async def _initialize_matlab_proxy_with_mpm(self, _logger: Logger): response = await mpm_lib.start_matlab_proxy_for_kernel( caller_id=self.kernel_id, parent_id=self.parent_pid, - is_shared_matlab=True, + is_shared_matlab=self.is_shared_matlab, base_url_prefix=self.jupyter_base_url, ) err = response.get("errors") diff --git a/src/jupyter_matlab_kernel/mwi_comm_helpers.py b/src/jupyter_matlab_kernel/mwi_comm_helpers.py index 95df8105..f3f54281 100644 --- a/src/jupyter_matlab_kernel/mwi_comm_helpers.py +++ b/src/jupyter_matlab_kernel/mwi_comm_helpers.py @@ -4,6 +4,8 @@ import http import json import pathlib +from dataclasses import dataclass +from typing import Optional import aiohttp from matlab_proxy.util.mwi.embedded_connector.helpers import ( @@ -27,6 +29,27 @@ def check_licensing_status(data): return licensing_status +@dataclass +class MATLABStatus: + """Represents the status of MATLAB and its licensing information. + + Attributes: + is_matlab_licensed (bool): Indicates whether MATLAB is properly licensed. + matlab_status (str): Current status of the MATLAB instance. + matlab_proxy_has_error (bool): Whether the MATLAB proxy has encountered an error. Defaults to False. + licensing_mode (str): The type of licensing being used. Defaults to an empty string. + matlab_version (str): Version of the MATLAB instance. Defaults to an empty string. + matlab_root_path (str): Root installation path of MATLAB. Defaults to an empty string. + """ + + is_matlab_licensed: bool + matlab_status: str + matlab_proxy_has_error: bool = False + licensing_mode: str = "" + matlab_version: str = "" + matlab_root_path: str = "" + + class MWICommHelper: def __init__( self, kernel_id, url, shell_loop, control_loop, headers=None, logger=_logger @@ -88,35 +111,84 @@ async def disconnect(self): if self._http_control_client: await self._http_control_client.close() - async def fetch_matlab_proxy_status(self): + async def fetch_matlab_root_path(self) -> Optional[str]: + """ + Fetches the MATLAB root path from the matlab-proxy server. + + Sends an HTTP GET request to the /get_env_config endpoint of matlab-proxy + to retrieve the filesystem path to the MATLAB installation. + + Returns: + Optional[str]: The filesystem path to the MATLAB installation root directory, + or None if the path could not be retrieved. + """ + self.logger.debug("Fetching MATLAB root path from matlab-proxy") + resp = await self._http_shell_client.get(self.url + "/get_env_config") + self.logger.debug( + f"Received status code for matlab-proxy get-env-config request: {resp.status}" + ) + + if resp.status == http.HTTPStatus.OK: + data = await resp.json() + self.logger.debug(f"get-env-config data:\n{data}") + matlab_data = data.get("matlab") or {} + return matlab_data.get("rootPath", None) + + self.logger.warning( + "Error occurred during retrieving environment config for matlab-proxy" + ) + return None + + async def fetch_matlab_proxy_status(self) -> Optional[MATLABStatus]: """ - Sends HTTP request to /get_status endpoint of matlab-proxy and returns - license and MATLAB status. + Fetches the current status of the MATLAB proxy server. + + Sends an HTTP GET request to the /get_status endpoint of matlab-proxy + to retrieve information about MATLAB licensing, runtime status, and any + errors that may have occurred. Returns: - Tuple (bool, string): - is_matlab_licensed (bool): True if matlab-proxy has license information, else False. - matlab_status (string): Status of MATLAB. Values could be "up", "down" and "starting" - matlab_proxy_has_error (bool): True if matlab-proxy faced any issues and unable to - start MATLAB + Optional[MATLABStatus]: A MATLABStatus object containing: + - is_matlab_licensed (bool): True if MATLAB has valid license + information, False otherwise. + - matlab_status (str): Current MATLAB state. Possible values: + "up" (running), "down" (stopped). + - matlab_proxy_has_error (bool): True if matlab-proxy encountered + errors preventing MATLAB startup, False otherwise. + - licensing_mode (str): The type of licensing being used + (e.g., "mhlm", "nlm", "existing_license"). + - matlab_version (str): The version string of the MATLAB installation + (e.g., "R2024a"). + Raises: - HTTPError: Occurs when connection to matlab-proxy cannot be established. + HTTPError: If the HTTP request fails or matlab-proxy returns a + non-200 status code, indicating connection issues or + server errors. + + Example: + >>> status = await comm_helper.fetch_matlab_proxy_status() + >>> if status and status.matlab_status == "up": + ... print(f"MATLAB {status.matlab_version} is running") """ self.logger.debug("Fetching matlab-proxy status") resp = await self._http_shell_client.get(self.url + "/get_status") self.logger.debug(f"Received status code: {resp.status}") if resp.status == http.HTTPStatus.OK: data = await resp.json() - self.logger.debug(f"Response:\n{data}") - is_matlab_licensed = check_licensing_status(data) - - matlab_status = data["matlab"]["status"] - matlab_proxy_has_error = data["error"] is not None - return is_matlab_licensed, matlab_status, matlab_proxy_has_error + self.logger.debug(f"matlab-proxy status:\n{data}") + matlab_data = data.get("matlab") or {} + return MATLABStatus( + is_matlab_licensed=check_licensing_status(data), + matlab_status=matlab_data.get("status", ""), + matlab_proxy_has_error=data.get("error") is not None, + licensing_mode=(data.get("licensing") or {}).get("type", ""), + matlab_version=matlab_data.get("version", ""), + ) else: self.logger.error("Error occurred during communication with matlab-proxy") resp.raise_for_status() + return None async def send_execution_request_to_matlab(self, code): """ @@ -281,7 +353,7 @@ async def _send_feval_request_to_matlab(self, http_client, fname, nargout, *args ) else: self.logger.error( - f'Error during execution of FEval request in MATLAB:\n{feval_response["messageFaults"][0]["message"]}' + f"Error during execution of FEval request in MATLAB:\n{feval_response['messageFaults'][0]['message']}" ) error_message = "Failed to execute. Please try again." raise Exception(error_message) diff --git a/src/jupyter_matlab_kernel/mwi_exceptions.py b/src/jupyter_matlab_kernel/mwi_exceptions.py index 58671158..da45bc46 100644 --- a/src/jupyter_matlab_kernel/mwi_exceptions.py +++ b/src/jupyter_matlab_kernel/mwi_exceptions.py @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. # Custom Exceptions used in MATLAB Kernel @@ -38,5 +38,5 @@ class MATLABConnectionError(Exception): def __init__(self, message=None): if message is None: - message = 'Error connecting to MATLAB. Check the status of MATLAB by clicking the "Open MATLAB" button. Retry after ensuring MATLAB is running successfully' + message = 'Error connecting to MATLAB. Check the status of MATLAB by clicking the "Open MATLAB" button. Retry after ensuring MATLAB is running successfully.' super().__init__(message) diff --git a/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts b/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts index abefac94..d0cc0dfd 100644 --- a/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts +++ b/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts @@ -12,18 +12,77 @@ import { PageConfig } from '@jupyterlab/coreutils'; import { DocumentRegistry } from '@jupyterlab/docregistry'; import { INotebookModel, NotebookPanel } from '@jupyterlab/notebook'; -import { IDisposable } from '@lumino/disposable'; +import { DisposableDelegate } from '@lumino/disposable'; import { matlabIcon } from '../icons'; +function createMATLABToolbarButton (targetUrl: string): ToolbarButton { + return new ToolbarButton({ + className: 'openMATLABButton matlab-toolbar-button-spaced', + icon: matlabIcon, + label: 'Open MATLAB', + tooltip: 'Open MATLAB', + onClick: (): void => { + window.open(targetUrl, '_blank'); + } + }); +} + /** Wait until the kernel has loaded, then check if it is a MATLAB kernel. */ -export const insertButton = async ( - panel: NotebookPanel, - matlabToolbarButton: ToolbarButton -): Promise => { - await panel.sessionContext.ready; - if (panel.sessionContext.kernelDisplayName === 'MATLAB Kernel') { - panel.toolbar.insertItem(10, 'matlabToolbarButton', matlabToolbarButton); +export const insertButton = async (panel: NotebookPanel): Promise => { + try { + await panel.sessionContext.ready; + let targetUrl = ''; + let matlabToolbarButton: ToolbarButton | null = null; + + // Function to update the target URL based on kernel ID + const updateTargetUrl = (): void => { + // Check if the kernel is a MATLAB Kernel + if (panel.sessionContext.kernelDisplayName === 'MATLAB Kernel') { + let kernelId = ''; + + // Check that session and kernel exist and then retrieve kernel ID + if (panel.sessionContext.session && panel.sessionContext.session.kernel) { + kernelId = panel.sessionContext.session.kernel.id; + } + + if (kernelId !== '') { + targetUrl = PageConfig.getBaseUrl() + 'matlab/' + kernelId + '/'; + + // Create the button if it doesn't exist yet + if (!matlabToolbarButton) { + matlabToolbarButton = createMATLABToolbarButton(targetUrl); + panel.toolbar.insertItem(10, 'matlabToolbarButton', matlabToolbarButton); + } else { + // Update the button's onClick handler + matlabToolbarButton.onClick = () => { + window.open(targetUrl, '_blank'); + }; + } + } + } + }; + + // Create Open MATLAB toolbar button + updateTargetUrl(); + + // Listen for kernel changes + panel.sessionContext.kernelChanged.connect(() => { + updateTargetUrl(); + }); + + // Create a disposable that will clean up the listener + return new DisposableDelegate(() => { + if (matlabToolbarButton) { + matlabToolbarButton.dispose(); + } + panel.sessionContext.kernelChanged.disconnect(() => { + updateTargetUrl(); + }); + }); + } catch (error) { + console.error('Failed to insert MATLAB toolbar button: ', error); + return new DisposableDelegate(() => {}); } }; @@ -32,21 +91,13 @@ implements DocumentRegistry.IWidgetExtension { createNew ( panel: NotebookPanel, context: DocumentRegistry.IContext - ): IDisposable { + ): DisposableDelegate { /** Create the toolbar button to open MATLAB in a browser. */ - const matlabToolbarButton = new ToolbarButton({ - className: 'openMATLABButton', - icon: matlabIcon, - label: 'Open MATLAB', - tooltip: 'Open MATLAB', - onClick: (): void => { - const baseUrl = PageConfig.getBaseUrl(); - // "_blank" is the option to open in a new browser tab - window.open(baseUrl + 'matlab', '_blank'); - } + insertButton(panel).catch(error => { + console.error('Error inserting MATLAB toolbar button:', error); }); - insertButton(panel, matlabToolbarButton); - return matlabToolbarButton; + // Return a dummy disposable immediately + return new DisposableDelegate(() => {}); } } diff --git a/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts b/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts index 97fff9c9..c2167ac3 100644 --- a/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts +++ b/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts @@ -9,6 +9,7 @@ import { import { NotebookPanel, INotebookModel } from '@jupyterlab/notebook'; import { JupyterFrontEnd } from '@jupyterlab/application'; import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { Signal } from '@lumino/signaling'; jest.mock('../icons', () => ({ matlabIcon: { @@ -17,9 +18,6 @@ jest.mock('../icons', () => ({ } })); -// Get the mocked matlabIcon -const { matlabIcon } = jest.requireMock('../icons'); - // Mock JupyterLab dependencies jest.mock('@jupyterlab/apputils', () => ({ ToolbarButton: jest.fn().mockImplementation((options: any) => ({ @@ -38,36 +36,34 @@ jest.mock('@jupyterlab/coreutils', () => ({ const originalWindowOpen = window.open; window.open = jest.fn(); -// Mock for NotebookPanel -const createMockNotebookPanel = (kernelDisplayName = 'MATLAB Kernel') => ({ - sessionContext: { - ready: Promise.resolve(), - kernelDisplayName, - session: null, - initialize: jest.fn(), - isReady: true, - isTerminating: false, - // Add other required methods as - dispose: jest.fn() - }, - toolbar: { - insertItem: jest.fn(), - names: [] - // addItem: jest.fn(), - // insertAfter: jest.fn(), - // insertBefore: jest.fn() - } -}); - -// Mock for ToolbarButton -const createMockToolbarButton = () => ({ - className: 'openMATLABButton', - icon: matlabIcon, - label: 'Open MATLAB', - tooltip: 'Open MATLAB', - onClick: expect.any(Function), - dispose: jest.fn() -}); +// Mock for NotebookPanel with kernel change signal +const createMockNotebookPanel = (kernelDisplayName = 'MATLAB Kernel', kernelId = '12345') => { + const kernelChangedSignal = new Signal({}); + + return { + sessionContext: { + ready: Promise.resolve(), + kernelDisplayName, + session: kernelId + ? { + kernel: { + id: kernelId + } + } + : null, + kernelChanged: kernelChangedSignal, + initialize: jest.fn(), + isReady: true, + isTerminating: false, + // Add other required methods as + dispose: jest.fn() + }, + toolbar: { + insertItem: jest.fn(), + names: [] + } + }; +}; // Mock for JupyterFrontEnd const createMockJupyterFrontEnd = () => ({ @@ -91,34 +87,84 @@ describe('matlab_browser_button', () => { }); describe('insertButton', () => { - test('should insert button when kernel is MATLAB Kernel', async () => { + test('should insert button when kernel is MATLAB Kernel with valid kernel ID', async () => { // Arrange - const panel = createMockNotebookPanel('MATLAB Kernel'); - const button = createMockToolbarButton(); + const panel = createMockNotebookPanel('MATLAB Kernel', 'test-kernel-123'); // Act - await insertButton(panel as unknown as NotebookPanel, button as any); + await insertButton(panel as unknown as NotebookPanel); // Assert expect(panel.toolbar!.insertItem).toHaveBeenCalledWith( 10, 'matlabToolbarButton', - button + expect.objectContaining({ + className: 'openMATLABButton matlab-toolbar-button-spaced', + label: 'Open MATLAB' + }) ); }); test('should not insert button when kernel is not MATLAB Kernel', async () => { // Arrange const panel = createMockNotebookPanel('Python 3'); - const button = createMockToolbarButton(); // Act - await insertButton(panel as unknown as NotebookPanel, button as any); + await insertButton(panel as unknown as NotebookPanel); // Assert expect(panel.toolbar!.insertItem).not.toHaveBeenCalled(); }); + test('should not insert button when kernel ID is empty', async () => { + const panel = createMockNotebookPanel('MATLAB Kernel', ''); + + await insertButton(panel as unknown as NotebookPanel); + + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + }); + + test('should not insert button when session is null', async () => { + const panel = createMockNotebookPanel('MATLAB Kernel'); + panel.sessionContext.session = null; + + await insertButton(panel as unknown as NotebookPanel); + + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + }); + + test('should not insert button when kernel is null', async () => { + const panel = createMockNotebookPanel('MATLAB Kernel'); + panel.sessionContext.session = { kernel: null as any }; + + await insertButton(panel as unknown as NotebookPanel); + + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + }); + + test('should construct correct target URL with kernel ID', async () => { + const ToolbarButtonMock = jest.requireMock('@jupyterlab/apputils').ToolbarButton; + let capturedOnClick: () => void = () => {}; + + ToolbarButtonMock.mockImplementationOnce((options: any) => { + capturedOnClick = options.onClick; + return { + ...options, + dispose: jest.fn() + }; + }); + + const panel = createMockNotebookPanel('MATLAB Kernel', 'kernel-abc-123'); + await insertButton(panel as unknown as NotebookPanel); + + capturedOnClick(); + + expect(window.open).toHaveBeenCalledWith( + 'http://localhost:8888/matlab/kernel-abc-123/', + '_blank' + ); + }); + test('should wait for session context to be ready before checking kernel', async () => { // Arrange const readyPromise = new Promise((resolve) => @@ -127,16 +173,21 @@ describe('matlab_browser_button', () => { const panel = { sessionContext: { ready: readyPromise, - kernelDisplayName: 'MATLAB Kernel' + kernelDisplayName: 'MATLAB Kernel', + session: { + kernel: { + id: 'test-kernel' + } + }, + kernelChanged: new Signal({}) }, toolbar: { insertItem: jest.fn() } }; - const button = createMockToolbarButton(); // Act - const insertPromise = insertButton(panel as any, button as any); + const insertPromise = insertButton(panel as any); // Assert - insertItem should not be called before ready resolves expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); @@ -145,11 +196,50 @@ describe('matlab_browser_button', () => { await insertPromise; // Now insertItem should have been called - expect(panel.toolbar.insertItem).toHaveBeenCalledWith( - 10, - 'matlabToolbarButton', - button + expect(panel.toolbar.insertItem).toHaveBeenCalled(); + }); + + test('should update button onClick when kernel changes', async () => { + const ToolbarButtonMock = jest.requireMock('@jupyterlab/apputils').ToolbarButton; + const mockButton = { + onClick: jest.fn(), + dispose: jest.fn() + }; + + ToolbarButtonMock.mockReturnValue(mockButton); + + const panel = createMockNotebookPanel('MATLAB Kernel', 'kernel-1'); + await insertButton(panel as unknown as NotebookPanel); + + // Simulate kernel change + panel.sessionContext.session = { + kernel: { id: 'kernel-2' } + }; + panel.sessionContext.kernelChanged.emit({}); + + // Wait for async operations + await new Promise(resolve => setTimeout(resolve, 0)); + + expect(mockButton.onClick).toBeDefined(); + }); + + test('should handle errors gracefully', async () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + const panel = { + sessionContext: { + ready: Promise.reject(new Error('Session failed')) + } + }; + + const result = await insertButton(panel as any); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + 'Failed to insert MATLAB toolbar button: ', + expect.any(Error) ); + expect(result.dispose).toBeDefined(); + + consoleErrorSpy.mockRestore(); }); }); @@ -160,25 +250,10 @@ describe('matlab_browser_button', () => { beforeEach(() => { extension = new MatlabToolbarButtonExtension(); - panel = createMockNotebookPanel(); + panel = createMockNotebookPanel('MATLAB Kernel', 'test-kernel'); context = {}; }); - test('should create a toolbar button with correct properties', () => { - // Act - const result = extension.createNew(panel, context); - - // Assert - expect(result).toEqual( - expect.objectContaining({ - className: 'openMATLABButton', - icon: matlabIcon, - label: 'Open MATLAB', - tooltip: 'Open MATLAB' - }) - ); - }); - test('should return a disposable object', () => { // Act const result = extension.createNew(panel, context); @@ -188,35 +263,20 @@ describe('matlab_browser_button', () => { expect(typeof result.dispose).toBe('function'); }); - test('button onClick should open MATLAB in a new tab', () => { - // Arrange - const ToolbarButtonMock = jest.requireMock( - '@jupyterlab/apputils' - ).ToolbarButton; - let capturedOnClick: () => void = () => {}; // Initialize with empty function - - // Capture the onClick handler when ToolbarButton is constructed - ToolbarButtonMock.mockImplementationOnce((options: any) => { - capturedOnClick = options.onClick; - return { - ...options, - dispose: jest.fn() - }; - }); + test('should call insertButton when createNew is invoked', () => { + const matlabButtonModule = require('../plugins/matlabToolbarButton'); + const spy = jest + .spyOn(matlabButtonModule, 'insertButton') + .mockResolvedValue({ dispose: jest.fn() }); - // Act extension.createNew( - panel as unknown as NotebookPanel, - context as unknown as DocumentRegistry.IContext + panel as unknown as NotebookPanel, + context as unknown as DocumentRegistry.IContext ); - // Manually call the onClick handler - capturedOnClick(); - // Assert - expect(window.open).toHaveBeenCalledWith( - 'http://localhost:8888/matlab', - '_blank' - ); + expect(spy).toHaveBeenCalledWith(panel); + + spy.mockRestore(); }); test('should call insertButton with panel and button', () => { @@ -228,13 +288,13 @@ describe('matlab_browser_button', () => { .mockImplementation(() => Promise.resolve()); // Act - const button = extension.createNew( - panel as unknown as NotebookPanel, - context as unknown as DocumentRegistry.IContext + extension.createNew( + panel as unknown as NotebookPanel, + context as unknown as DocumentRegistry.IContext ); // Assert - expect(spy).toHaveBeenCalledWith(panel, button); + expect(spy).toHaveBeenCalledWith(panel); // Cleanup spy.mockRestore(); diff --git a/src/jupyter_matlab_labextension/style/base.css b/src/jupyter_matlab_labextension/style/base.css index e11f4577..de8fe6aa 100644 --- a/src/jupyter_matlab_labextension/style/base.css +++ b/src/jupyter_matlab_labextension/style/base.css @@ -1,5 +1,11 @@ +/* # Copyright 2024-2025 The MathWorks, Inc. */ /* See the JupyterLab Developer Guide for useful CSS Patterns: https://jupyterlab.readthedocs.io/en/stable/developer/css.html */ + +/* Adds spacing between toolbar icon and label */ +.matlab-toolbar-button-spaced .jp-ToolbarButtonComponent-label { + margin-left: 4px; +} \ No newline at end of file diff --git a/tests/integration/utils/integration_test_utils.py b/tests/integration/utils/integration_test_utils.py index 40a1be08..9f37daf2 100644 --- a/tests/integration/utils/integration_test_utils.py +++ b/tests/integration/utils/integration_test_utils.py @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. # Utility functions for integration testing of jupyter-matlab-proxy import asyncio @@ -24,7 +24,7 @@ def perform_basic_checks(): # Check if MATLAB is in the system path assert matlab_path is not None, "MATLAB is not in system path" - # Check if MATLAB verison is >= R2020b + # Check if MATLAB version is >= R2020b assert ( matlab_proxy.settings.get_matlab_version(matlab_path) >= "R2020b" ), "MATLAB version should be R2020b or later" @@ -83,35 +83,33 @@ async def wait_matlab_proxy_ready(matlab_proxy_url): from jupyter_matlab_kernel.mwi_comm_helpers import MWICommHelper - is_matlab_licensed = False - matlab_status = "down" start_time = time.time() loop = asyncio.get_event_loop() - matlab_proxy = MWICommHelper("", matlab_proxy_url, loop, loop, {}) - await matlab_proxy.connect() + comm_helper = MWICommHelper("", matlab_proxy_url, loop, loop, {}) + await comm_helper.connect() + matlab_proxy_status = await comm_helper.fetch_matlab_proxy_status() # Poll for matlab-proxy to be up - while matlab_status in ["down", "starting"] and ( - time.time() - start_time < MATLAB_STARTUP_TIMEOUT + while ( + matlab_proxy_status + and matlab_proxy_status.matlab_status in ["down", "starting"] + and (time.time() - start_time < MATLAB_STARTUP_TIMEOUT) + and not matlab_proxy_status.matlab_proxy_has_error ): time.sleep(1) try: - ( - is_matlab_licensed, - matlab_status, - _, - ) = await matlab_proxy.fetch_matlab_proxy_status() + matlab_proxy_status = await comm_helper.fetch_matlab_proxy_status() except Exception: # The network connection can be flaky while the # matlab-proxy server is booting. There can also be some # intermediate connection errors pass - assert is_matlab_licensed is True, "MATLAB is not licensed" + assert matlab_proxy_status.is_matlab_licensed is True, "MATLAB is not licensed" assert ( - matlab_status == "up" - ), f"matlab-proxy process did not start successfully\nMATLAB Status is '{matlab_status}'" - await matlab_proxy.disconnect() + matlab_proxy_status.matlab_status == "up" + ), f"matlab-proxy process did not start successfully\nMATLAB Status is '{matlab_proxy_status.matlab_status}'" + await comm_helper.disconnect() def get_random_free_port() -> str: @@ -184,13 +182,14 @@ def license_matlab_proxy(matlab_proxy_url): status_info, "Verify if Licensing is successful. This might fail if incorrect credentials are provided", ).to_be_visible(timeout=60000) - except: + except Exception as e: # Grab screenshots log_dir = "./" file_name = "licensing-screenshot-failed.png" file_path = os.path.join(log_dir, file_name) os.makedirs(log_dir, exist_ok=True) page.screenshot(path=file_path) + print("Exception: %s", str(e)) finally: browser.close() diff --git a/tests/unit/jupyter_matlab_kernel/magics/test_matlab.py b/tests/unit/jupyter_matlab_kernel/magics/test_matlab.py new file mode 100644 index 00000000..e40efa1c --- /dev/null +++ b/tests/unit/jupyter_matlab_kernel/magics/test_matlab.py @@ -0,0 +1,202 @@ +# Copyright 2025 The MathWorks, Inc. + +import pytest + +from jupyter_matlab_kernel.magics.help import help +from jupyter_matlab_kernel.magics.matlab import ( + CMD_INFO, + CMD_NEW_SESSION, + DEDICATED_SESSION_CONFIRMATION_MSG, + EXISTING_NEW_SESSION_ERROR, + get_kernel_info, + handle_new_matlab_session, + matlab, +) +from jupyter_matlab_kernel.mwi_exceptions import MagicError + + +def test_help_magic(): + magic_object = help([matlab.__name__]) + before_cell_executor = magic_object.before_cell_execute() + output = next(before_cell_executor) + expected_output = matlab.info_about_magic + assert expected_output in output["value"][0] + + +@pytest.mark.parametrize( + "parameters", + [ + pytest.param([], id="atleast one argument is required"), + pytest.param( + ["invalid"], + id="Invalid argument should throw exception", + ), + pytest.param( + [CMD_INFO, CMD_NEW_SESSION], + id="more than one parameter should throw exception", + ), + ], +) +def test_matlab_magic_exceptions(parameters): + magic_object = matlab(parameters) + before_cell_executor = magic_object.before_cell_execute() + with pytest.raises(MagicError): + next(before_cell_executor) + + +@pytest.mark.parametrize( + "parameters, parameter_pos, cursor_pos, expected_output", + [ + pytest.param( + ["n"], + 1, + 1, + {"new_session"}, + id="n as parameter with parameter and cursor position as 1", + ), + pytest.param( + [""], + 1, + 1, + {"new_session", "info"}, + id="no parameter with parameter and cursor position as 1", + ), + pytest.param( + ["in"], + 1, + 2, + {"info"}, + id="i as parameter with parameter position as 1 and cursor position as 2", + ), + pytest.param( + ["i"], + 2, + 1, + set([]), + id="t as parameter with parameter position as 2 and cursor position as 1", + ), + pytest.param( + ["magic"], + 1, + 4, + set([]), + id="magic as parameter with parameter position as 1 and cursor position as 4", + ), + ], +) +def test_do_complete_in_matlab_magic( + parameters, parameter_pos, cursor_pos, expected_output +): + magic_object = matlab() + output = magic_object.do_complete(parameters, parameter_pos, cursor_pos) + assert expected_output.issubset(set(output)) + + +async def test_new_session_in_matlab_magic_while_already_in_new_session(mocker): + """ + Test that an appropriate message is displayed when trying to create a new session while already in a new session. + + This test verifies that when a %%matlab magic command with new_session option is executed + while MATLAB is already assigned to the kernel in a new session, an appropriate error message + is displayed indicating that the notebook is already linked to a new MATLAB session. + """ + mock_kernel = mocker.MagicMock() + mock_kernel.is_matlab_assigned = True + mock_kernel.is_shared_matlab = False + output = [] + async for result in handle_new_matlab_session(mock_kernel): + output.append(result) + + assert output is not None + assert EXISTING_NEW_SESSION_ERROR in output[0]["value"][0] + + +async def test_new_session_in_matlab_magic_while_already_in_shared_session(mocker): + """ + Test that an exception is raised when trying to switch from shared session to new session. + + This test verifies that when a %%matlab magic command with new_session option is executed + while MATLAB is already assigned to the kernel, an appropriate exception is raised + with a message indicating that the notebook is already linked to a MATLAB session. + """ + mock_kernel = mocker.MagicMock() + mock_kernel.is_matlab_assigned = True + mock_kernel.is_shared_matlab = True + with pytest.raises(Exception) as excinfo: + async for _ in handle_new_matlab_session(mock_kernel): + pass + + assert "linked to Default MATLAB session" in str(excinfo.value) + + +async def test_handle_new_matlab_session_success(mocker): + """ + Test that MATLAB proxy is started correctly when using MATLAB magic command. + + This test verifies that when a %%matlab magic command with new_session option is executed, + the kernel properly starts the MATLAB proxy, assigns MATLAB to the kernel + (is_matlab_assigned=True), and sets the shared MATLAB flag to False. + """ + mock_kernel = mocker.AsyncMock() + mock_kernel.is_matlab_assigned = False + output = [] + async for result in handle_new_matlab_session(mock_kernel): + output.append(result) + + assert output is not None + mock_kernel.start_matlab_proxy_and_comm_helper.assert_called_once() + mock_kernel.perform_startup_checks.assert_called_once() + mock_kernel.cleanup_matlab_proxy.assert_not_called() + assert mock_kernel.is_matlab_assigned is True + assert mock_kernel.is_shared_matlab is False + assert DEDICATED_SESSION_CONFIRMATION_MSG in output[0]["value"][0] + + +async def test_handle_new_matlab_session_raises_exception(mocker): + """ + Test that exceptions during MATLAB magic command execution are handled properly. + + This test verifies that when an exception occurs during the startup of the MATLAB proxy + (triggered by a %%matlab magic command), the kernel properly handles the error and + maintains the expected state (is_matlab_assigned=False, is_shared_matlab=True). + """ + mock_kernel = mocker.AsyncMock() + mock_kernel.is_matlab_assigned = False + output = [] + with pytest.raises(Exception): + async for result in handle_new_matlab_session(mock_kernel): + output.append(result) + + assert output is not None + mock_kernel.start_matlab_proxy_and_comm_helper.assert_called_once() + mock_kernel.perform_startup_checks.side_effect = Exception( + "MATLAB Connection Error" + ) + mock_kernel.cleanup_matlab_proxy.assert_called_once() + assert mock_kernel.is_matlab_assigned is False + assert mock_kernel.is_shared_matlab is True + + +@pytest.mark.parametrize( + "shared_matlab_status, expected_output", + [ + (True, "MATLAB Shared With Other Notebooks: True"), + (False, "MATLAB Shared With Other Notebooks: False"), + ], + ids=["Shared MATLAB", "Non-shared MATLAB"], +) +async def test_get_kernel_info_in_matlab_magic( + shared_matlab_status, expected_output, mocker +): + mock_kernel = mocker.MagicMock() + mock_kernel._get_kernel_info.return_value = { + "is_shared_matlab": shared_matlab_status, + "matlab_version": "R2025b", + "matlab_root_path": "/path/to/matlab", + "licensing_mode": "existing_license", + } + output = [] + async for result in get_kernel_info(mock_kernel): + output.append(result) + assert output is not None + assert expected_output in output[0]["value"][0] diff --git a/tests/unit/jupyter_matlab_kernel/test_kernel.py b/tests/unit/jupyter_matlab_kernel/test_kernel.py index 18ef551b..d0591ad6 100644 --- a/tests/unit/jupyter_matlab_kernel/test_kernel.py +++ b/tests/unit/jupyter_matlab_kernel/test_kernel.py @@ -6,10 +6,8 @@ from jupyter_server import serverapp from mocks.mock_jupyter_server import MockJupyterServerFixture -from jupyter_matlab_kernel.jsp_kernel import ( - MATLABKernelUsingJSP, - start_matlab_proxy, -) +from jupyter_matlab_kernel.jsp_kernel import start_matlab_proxy +from jupyter_matlab_kernel.mpm_kernel import MATLABKernelUsingMPM from jupyter_matlab_kernel.mwi_exceptions import MATLABConnectionError @@ -98,19 +96,24 @@ async def test_matlab_not_licensed_non_jupyter(mocker): exception (MATLABConnectionError) is raised when performing startup checks. """ # Mock the kernel's jupyter_base_url attribute to simulate a non-Jupyter environment - kernel = mocker.MagicMock(spec=MATLABKernelUsingJSP) + kernel = mocker.MagicMock(spec=MATLABKernelUsingMPM) kernel.jupyter_base_url = None kernel.startup_error = None + kernel.matlab_proxy_base_url = "/test" + + matlab_status_mock = mocker.Mock() + matlab_status_mock.is_matlab_licensed = False + matlab_status_mock.matlab_status = "down" + matlab_status_mock.matlab_proxy_has_error = False + kernel.mwi_comm_helper = mocker.Mock() kernel.mwi_comm_helper.fetch_matlab_proxy_status = mocker.AsyncMock( - return_value=(False, "down", False) + return_value=matlab_status_mock ) # Mock the perform_startup_checks method to actually call the implementation - async def mock_perform_startup_checks(*args, **kwargs): - return await MATLABKernelUsingJSP.perform_startup_checks( - kernel, *args, **kwargs - ) + async def mock_perform_startup_checks(): + return await MATLABKernelUsingMPM.perform_startup_checks(kernel) kernel.perform_startup_checks.side_effect = mock_perform_startup_checks diff --git a/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py b/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py index 1bf01d1b..f797d376 100644 --- a/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py +++ b/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py @@ -26,7 +26,6 @@ def mpm_kernel_instance(mocker) -> MATLABKernelUsingMPM: return MATLABKernelUsingMPM() -@pytest.mark.asyncio async def test_initialize_matlab_proxy_with_mpm_success(mocker, mpm_kernel_instance): mpm_lib_start_matlab_proxy_response = { "absolute_url": "dummyURL", @@ -166,3 +165,27 @@ async def test_do_shutdown_exception(mocker, mpm_kernel_instance): mpm_kernel_instance.mpm_auth_token, ) assert not mpm_kernel_instance.is_matlab_assigned + + +async def test_matlab_proxy_assignment_on_executing_matlab_command( + mocker, mpm_kernel_instance +): + """ + Test that MATLAB proxy is assigned when executing a MATLAB command. + + This test verifies that when a regular MATLAB command is executed while MATLAB + is not yet assigned, the kernel properly starts the MATLAB proxy and assigns + MATLAB to the kernel (is_matlab_assigned=True). + """ + code = "why" + mpm_kernel_instance.is_matlab_assigned = False + # Patch kernel instance to mock start_matlab_proxy_and_comm_helper method + mock_start_matlab_proxy = mocker.patch.object( + mpm_kernel_instance, + "start_matlab_proxy_and_comm_helper", + autospec=True, + ) + + await mpm_kernel_instance.do_execute(code, silent=True) + mock_start_matlab_proxy.assert_called_once() + assert mpm_kernel_instance.is_matlab_assigned is True diff --git a/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py b/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py index 4b7ee6f4..786dbd47 100644 --- a/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py +++ b/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py @@ -23,7 +23,7 @@ @pytest.fixture -async def matlab_proxy_fixture(): +async def comm_helper_fixture(): url = "http://localhost" headers = {} kernel_id = "" @@ -36,7 +36,7 @@ async def matlab_proxy_fixture(): # Testing fetch_matlab_proxy_status async def test_fetch_matlab_proxy_status_unauth_request( - monkeypatch, matlab_proxy_fixture + monkeypatch, comm_helper_fixture ): """ This test checks that fetch_matlab_proxy_status throws an exception @@ -48,7 +48,7 @@ async def mock_get(*args, **kwargs): monkeypatch.setattr(aiohttp.ClientSession, "get", mock_get) with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.fetch_matlab_proxy_status() + await comm_helper_fixture.fetch_matlab_proxy_status() assert MockUnauthorisedRequestResponse().exception_msg in str(exceptionInfo.value) @@ -63,7 +63,7 @@ async def mock_get(*args, **kwargs): ], ) async def test_fetch_matlab_proxy_status( - input_lic_type, expected_license_status, monkeypatch, matlab_proxy_fixture + input_lic_type, expected_license_status, monkeypatch, comm_helper_fixture ): """ This test checks that fetch_matlab_proxy_status returns the correct @@ -77,17 +77,35 @@ async def mock_get(*args, **kwargs): monkeypatch.setattr(aiohttp.ClientSession, "get", mock_get) - ( - is_matlab_licensed, - matlab_status, - matlab_proxy_has_error, - ) = await matlab_proxy_fixture.fetch_matlab_proxy_status() - assert is_matlab_licensed == expected_license_status - assert matlab_status == "up" - assert matlab_proxy_has_error is False + matlab_proxy_status = await comm_helper_fixture.fetch_matlab_proxy_status() + assert matlab_proxy_status.is_matlab_licensed == expected_license_status + assert matlab_proxy_status.matlab_status == "up" + assert matlab_proxy_status.matlab_proxy_has_error is False -async def test_interrupt_request_bad_request(monkeypatch, matlab_proxy_fixture): +async def test_fetch_matlab_root_path(mocker, comm_helper_fixture): + """ + This test checks that fetch_matlab_root_path returns the correct matlab root path. + """ + mock_response = mocker.AsyncMock() + mock_response.status = http.HTTPStatus.OK + mock_response.json = mocker.AsyncMock( + return_value={ + "matlab": { + "rootPath": "test_root_path", + "version": "test_version", + } + }, + ) + mocker.patch( + "aiohttp.ClientSession.get", new=mocker.AsyncMock(return_value=mock_response) + ) + + matlab_root_path = await comm_helper_fixture.fetch_matlab_root_path() + assert matlab_root_path == "test_root_path" + + +async def test_interrupt_request_bad_request(monkeypatch, comm_helper_fixture): """ This test checks that send_interrupt_request_to_matlab raises an exception if the response to the HTTP post is not valid. @@ -101,12 +119,12 @@ async def mock_post(*args, **kwargs): monkeypatch.setattr(aiohttp.ClientSession, "post", mock_post) with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.send_interrupt_request_to_matlab() + await comm_helper_fixture.send_interrupt_request_to_matlab() assert mock_exception_message in str(exceptionInfo.value) # Testing send_execution_request_to_matlab -async def test_execution_request_bad_request(monkeypatch, matlab_proxy_fixture): +async def test_execution_request_bad_request(monkeypatch, comm_helper_fixture): """ This test checks that send_execution_request_to_matlab throws an exception if the response to the HTTP request is invalid. @@ -120,12 +138,12 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.send_execution_request_to_matlab(code) + await comm_helper_fixture.send_execution_request_to_matlab(code) assert mock_exception_message in str(exceptionInfo.value) async def test_execution_request_invalid_feval_response( - monkeypatch, matlab_proxy_fixture + monkeypatch, comm_helper_fixture ): """ This test checks that send_execution_request_to_matlab raises an exception @@ -150,11 +168,11 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" with pytest.raises(MATLABConnectionError) as exceptionInfo: - await matlab_proxy_fixture.send_execution_request_to_matlab(code) + await comm_helper_fixture.send_execution_request_to_matlab(code) assert str(exceptionInfo.value) == str(MATLABConnectionError()) -async def test_execution_interrupt(monkeypatch, matlab_proxy_fixture): +async def test_execution_interrupt(monkeypatch, comm_helper_fixture): """ This test checks that send_execution_request_to_matlab raises an exception if the matlab command appears to have been interupted. @@ -190,11 +208,11 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" with pytest.raises(Exception) as exceptionInfo: - await matlab_proxy_fixture.send_execution_request_to_matlab(code) + await comm_helper_fixture.send_execution_request_to_matlab(code) assert "Operation may have interrupted by user" in str(exceptionInfo.value) -async def test_execution_success(monkeypatch, matlab_proxy_fixture): +async def test_execution_success(monkeypatch, comm_helper_fixture): """ This test checks that send_execution_request_to_matlab returns the correct information from a valid response from MATLAB. @@ -225,7 +243,7 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" try: - outputs = await matlab_proxy_fixture.send_execution_request_to_matlab(code) + outputs = await comm_helper_fixture.send_execution_request_to_matlab(code) except Exception: pytest.fail("Unexpected failured in execution request") @@ -233,7 +251,7 @@ async def mock_post(*args, **kwargs): # Testing send_eval_request_to_matlab -async def test_send_eval_request_to_matlab_success(monkeypatch, matlab_proxy_fixture): +async def test_send_eval_request_to_matlab_success(monkeypatch, comm_helper_fixture): """Test that send_eval_request_to_matlab returns eval response correctly.""" # Arrange @@ -245,7 +263,7 @@ async def mock_post(*args, **kwargs): mcode = "x = 1 + 1" # Act - result = await matlab_proxy_fixture.send_eval_request_to_matlab(mcode) + result = await comm_helper_fixture.send_eval_request_to_matlab(mcode) # Assert # Verify the eval response is returned as-is @@ -253,9 +271,7 @@ async def mock_post(*args, **kwargs): assert result == expected_response -async def test_send_eval_request_to_matlab_with_error( - monkeypatch, matlab_proxy_fixture -): +async def test_send_eval_request_to_matlab_with_error(monkeypatch, comm_helper_fixture): """Test that send_eval_request_to_matlab returns error response correctly.""" # Arrange @@ -271,7 +287,7 @@ async def mock_post(*args, **kwargs): mcode = "invalid_syntax" # Act - result = await matlab_proxy_fixture.send_eval_request_to_matlab(mcode) + result = await comm_helper_fixture.send_eval_request_to_matlab(mcode) # Assert # Verify the error response is returned as-is @@ -284,7 +300,7 @@ async def mock_post(*args, **kwargs): async def test_send_eval_request_to_matlab_bad_request( - monkeypatch, matlab_proxy_fixture + monkeypatch, comm_helper_fixture ): """Test that send_eval_request_to_matlab raises exception for bad HTTP request.""" # Arrange @@ -299,14 +315,14 @@ async def mock_post(*args, **kwargs): # Act with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.send_eval_request_to_matlab(mcode) + await comm_helper_fixture.send_eval_request_to_matlab(mcode) # Assert assert mock_exception_message in str(exceptionInfo.value) async def test_send_eval_request_to_matlab_missing_eval_response( - monkeypatch, matlab_proxy_fixture + monkeypatch, comm_helper_fixture ): """Test that send_eval_request_to_matlab raises MATLABConnectionError for missing EvalResponse.""" @@ -318,11 +334,11 @@ async def mock_post(*args, **kwargs): mcode = "x = 1 + 1" with pytest.raises(MATLABConnectionError): - await matlab_proxy_fixture.send_eval_request_to_matlab(mcode) + await comm_helper_fixture.send_eval_request_to_matlab(mcode) # Testing _read_eval_response_from_file -async def test_read_eval_response_from_file_success_with_file(matlab_proxy_fixture): +async def test_read_eval_response_from_file_success_with_file(comm_helper_fixture): """Test _read_eval_response_from_file with successful response and file.""" # Arrange # Create a temporary file with test data @@ -341,7 +357,7 @@ async def test_read_eval_response_from_file_success_with_file(matlab_proxy_fixtu } # Act - result = await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) # Assert # Verify the result @@ -356,7 +372,7 @@ async def test_read_eval_response_from_file_success_with_file(matlab_proxy_fixtu os.remove(temp_file_path) -async def test_read_eval_response_from_file_success_without_file(matlab_proxy_fixture): +async def test_read_eval_response_from_file_success_without_file(comm_helper_fixture): """Test _read_eval_response_from_file with successful response but no file.""" # Arrange eval_response = { @@ -366,7 +382,7 @@ async def test_read_eval_response_from_file_success_without_file(matlab_proxy_fi } # Act - result = await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) # Assert # Verify empty result returns empty list @@ -374,7 +390,7 @@ async def test_read_eval_response_from_file_success_without_file(matlab_proxy_fi async def test_read_eval_response_from_file_error_with_message_faults( - matlab_proxy_fixture, + comm_helper_fixture, ): """Test _read_eval_response_from_file with error response containing message faults.""" # Arrange @@ -389,11 +405,11 @@ async def test_read_eval_response_from_file_error_with_message_faults( Exception, match="Failed to execute. Operation may have been interrupted by user.", ): - await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + await comm_helper_fixture._read_eval_response_from_file(eval_response) async def test_read_eval_response_from_file_error_without_message_faults( - matlab_proxy_fixture, + comm_helper_fixture, ): """Test _read_eval_response_from_file with error response without message faults.""" @@ -404,11 +420,11 @@ async def test_read_eval_response_from_file_error_without_message_faults( } with pytest.raises(Exception, match="Custom error message"): - await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + await comm_helper_fixture._read_eval_response_from_file(eval_response) async def test_read_eval_response_from_file_handles_file_deletion_error( - matlab_proxy_fixture, monkeypatch + comm_helper_fixture, monkeypatch ): """Test _read_eval_response_from_file handles file deletion errors gracefully.""" @@ -438,7 +454,7 @@ def mock_remove(path): } # Should not raise exception even if file deletion fails - result = await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) # Verify the result is still correct assert result == test_data @@ -450,7 +466,7 @@ def mock_remove(path): async def test_read_eval_response_from_file_with_empty_file_content( - matlab_proxy_fixture, + comm_helper_fixture, ): """Test _read_eval_response_from_file with empty file content.""" @@ -466,7 +482,7 @@ async def test_read_eval_response_from_file_with_empty_file_content( "messageFaults": [], } - result = await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) # Verify empty content returns empty list assert result == [] @@ -481,7 +497,7 @@ async def test_read_eval_response_from_file_with_empty_file_content( async def test_read_eval_response_from_file_with_whitespace_only_content( - matlab_proxy_fixture, + comm_helper_fixture, ): """Test _read_eval_response_from_file with whitespace-only file content.""" @@ -497,7 +513,7 @@ async def test_read_eval_response_from_file_with_whitespace_only_content( "messageFaults": [], } - result = await matlab_proxy_fixture._read_eval_response_from_file(eval_response) + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) # Verify whitespace-only content returns empty list assert result == [] From 426901a1e428cb0951814c5ae3bf687f16615dae Mon Sep 17 00:00:00 2001 From: Prabhakar Kumar Date: Thu, 11 Dec 2025 18:49:49 +0530 Subject: [PATCH 2/2] Update to v0.18.0 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 40813ca2..2b6370d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "jupyter-matlab-proxy" -version = "0.17.5" +version = "0.18.0" description = "MATLAB Integration for Jupyter" readme = "README.md" license = { file = "LICENSE.md" } @@ -43,7 +43,7 @@ dependencies = [ "ipykernel>=6.0.3", "jupyter-client", "jupyter-server-proxy>=4.1.0", - "matlab-proxy>=0.26.0", + "matlab-proxy>=0.30.0", "psutil", "requests", ]