From 8d5af741ee4bde29f8adf01a1ae6f7f1b628721a Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 19:16:51 +0300 Subject: [PATCH 01/16] Do not forbid extra fields in Pydantic models --- conftest.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/conftest.py b/conftest.py index 7e6dc39f..5938587a 100644 --- a/conftest.py +++ b/conftest.py @@ -83,9 +83,3 @@ def enterprise_config(pytestconfig): return core.HttpConfiguration(uri, api_key) else: pytest.skip("--enterprise-uri or --enterprise-api-key setting not found") - - -@pytest.fixture(scope="session", autouse=True) -def pydantic_forbid_extra_fields(): - """Fixture to disable allowing extra fields in our Pydantic models.""" - JsonModel.Config.extra = Extra.forbid From 7f393b7403e2a99ec9e9c0b4dd5c24c8a29db415 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 19:26:40 +0300 Subject: [PATCH 02/16] Pass hardcoded cert_path on Windows, so CA file is properly located when using the clients library from SLS notebooks --- .../clients/core/_jupyter_http_configuration.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 04bd8ea0..866ad8a8 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -3,6 +3,7 @@ """Implementation of JupyterHttpConfiguration.""" import os +import sys from nisystemlink.clients import core @@ -12,6 +13,7 @@ class JupyterHttpConfiguration(core.HttpConfiguration): _HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" _HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" + _SYSTEMLINK_SERVER_CERT_PATH = r"C:\ProgramData\National Instruments\Skyline\Certificates\http-server\http-server.cer" def __init__(self) -> None: """Initialize a configuration for SystemLink using API key-based @@ -20,6 +22,12 @@ def __init__(self) -> None: Raises: KeyError: if the expected environment variables are not set. """ - super().__init__( - os.environ[self._HTTP_URI_ENV_VAR], os.environ[self._HTTP_API_KEY_ENV_VAR] - ) + http_uri = os.environ[self._HTTP_URI_ENV_VAR] + api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] + + if sys.platform.startswith("win"): + super().__init__( + http_uri, api_key, cert_path=self._SYSTEMLINK_SERVER_CERT_PATH + ) + else: + super().__init__(http_uri, api_key) From 781b8c5bc1747c72b36e654b16ab1ed136eef4b3 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 19:44:10 +0300 Subject: [PATCH 03/16] Do not set cert_path in JupyterHttpConfiguration, when cert file does not exist, on Windows --- nisystemlink/clients/core/_jupyter_http_configuration.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 866ad8a8..0a391656 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -25,7 +25,13 @@ def __init__(self) -> None: http_uri = os.environ[self._HTTP_URI_ENV_VAR] api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] - if sys.platform.startswith("win"): + # When running SystemLink Server notebooks (Windows), pass Web Server CA certificate's path as the `cert_path`, + # if the file exists. This will allow clients created with the default configuration to use this CA certificate. + # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid `cert_path` + # will lead to errors. + if sys.platform.startswith("win") and os.path.exists( + self._SYSTEMLINK_SERVER_CERT_PATH + ): super().__init__( http_uri, api_key, cert_path=self._SYSTEMLINK_SERVER_CERT_PATH ) From 58b7728576050f28ea362f6268926e0a7e38d70d Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 19:52:17 +0300 Subject: [PATCH 04/16] Improve comment for the change --- nisystemlink/clients/core/_jupyter_http_configuration.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 0a391656..230f15a2 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -25,8 +25,13 @@ def __init__(self) -> None: http_uri = os.environ[self._HTTP_URI_ENV_VAR] api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] - # When running SystemLink Server notebooks (Windows), pass Web Server CA certificate's path as the `cert_path`, - # if the file exists. This will allow clients created with the default configuration to use this CA certificate. + # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder is + # no longer granted for scripts running under the Jupyter/NotebookExecution services. Since config files under + # HttpConfigurations are no longer readable, creating client objects from this library, from SLS notebooks, + # will default to using this JupyterHttpConfiguration. However, this does not set a `cert_path`, so HTTPS + # requests will fail. + # When running SystemLink Server notebooks (Windows), we will therefore pass the Web Server CA certificate's path as the + # `cert_path`, if the file exists. This will allow clients created with the default configuration to use this CA certificate. # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid `cert_path` # will lead to errors. if sys.platform.startswith("win") and os.path.exists( From 6e9850fd49ce8bb90aa4fc3b7b0c190c8dc1e8a0 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 19:54:12 +0300 Subject: [PATCH 05/16] Further improve comment explaining change --- nisystemlink/clients/core/_jupyter_http_configuration.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 230f15a2..081361d7 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -26,9 +26,9 @@ def __init__(self) -> None: api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder is - # no longer granted for scripts running under the Jupyter/NotebookExecution services. Since config files under - # HttpConfigurations are no longer readable, creating client objects from this library, from SLS notebooks, - # will default to using this JupyterHttpConfiguration. However, this does not set a `cert_path`, so HTTPS + # no longer granted for notebooks running under the JupyterHub/NotebookExecution services. Since config files under + # `HttpConfigurations` are no longer readable, creating client objects from this library, from SLS notebooks, + # will default to using this `JupyterHttpConfiguration`. However, this does not set a `cert_path`, so HTTPS # requests will fail. # When running SystemLink Server notebooks (Windows), we will therefore pass the Web Server CA certificate's path as the # `cert_path`, if the file exists. This will allow clients created with the default configuration to use this CA certificate. From 0e1517ae86d3197c69822d79821d80ab945e9d1e Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 21:33:06 +0300 Subject: [PATCH 06/16] Add unit tests for the change --- tests/core/test_jupyter_http_configuration.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 tests/core/test_jupyter_http_configuration.py diff --git a/tests/core/test_jupyter_http_configuration.py b/tests/core/test_jupyter_http_configuration.py new file mode 100644 index 00000000..717b3aa7 --- /dev/null +++ b/tests/core/test_jupyter_http_configuration.py @@ -0,0 +1,111 @@ +from nisystemlink.clients.core import JupyterHttpConfiguration + +from unittest.mock import patch +import os + +_HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" +_HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" +_SYSTEMLINK_SERVER_CERTIFICATE_PATH = r"C:\ProgramData\National Instruments\Skyline\Certificates\http-server\http-server.cer" +_SYSTEM_LINK_API_KEY_HEADER = "x-ni-api-key" + + +class TestJupyterHttpConfiguration: + def test__cert_file_exists_on_windows__cert_path_is_passed(self): + def mock_exists(path): + if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + return True + return False + + with patch("sys.platform", "win32"): + with patch("os.path.exists", side_effect=mock_exists): + with patch.dict( + os.environ, + { + _HTTP_URI_ENV_VAR: "https://my-uri", + _HTTP_API_KEY_ENV_VAR: "my-api-key", + }, + ): + config = JupyterHttpConfiguration() + assert config.server_uri == "https://my-uri" + assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.cert_path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH + + def test__cert_file_does_not_exist_on_windows__cert_path_is_not_passed(self): + def mock_exists(path): + if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + return False + return True + + with patch("sys.platform", "win32"): + with patch("os.path.exists", side_effect=mock_exists): + with patch.dict( + os.environ, + { + _HTTP_URI_ENV_VAR: "https://my-uri", + _HTTP_API_KEY_ENV_VAR: "my-api-key", + }, + ): + config = JupyterHttpConfiguration() + assert config.server_uri == "https://my-uri" + assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.cert_path is None + + def test__cert_file_does_not_exist_on_windows__cert_path_is_not_passed(self): + def mock_exists(path): + if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + return False + return True + + with patch("sys.platform", "win32"): + with patch("os.path.exists", side_effect=mock_exists): + with patch.dict( + os.environ, + { + _HTTP_URI_ENV_VAR: "https://my-uri", + _HTTP_API_KEY_ENV_VAR: "my-api-key", + }, + ): + config = JupyterHttpConfiguration() + assert config.server_uri == "https://my-uri" + assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.cert_path is None + + def test__cert_file_exists_on_linux__cert_path_is_not_passed(self): + def mock_exists(path): + if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + return True + return False + + with patch("sys.platform", "linux"): + with patch("os.path.exists", side_effect=mock_exists): + with patch.dict( + os.environ, + { + _HTTP_URI_ENV_VAR: "https://my-uri", + _HTTP_API_KEY_ENV_VAR: "my-api-key", + }, + ): + config = JupyterHttpConfiguration() + assert config.server_uri == "https://my-uri" + assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.cert_path is None + + def test__cert_file_does_not_exist_on_linux__cert_path_is_not_passed(self): + def mock_exists(path): + if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + return False + return True + + with patch("sys.platform", "linux"): + with patch("os.path.exists", side_effect=mock_exists): + with patch.dict( + os.environ, + { + _HTTP_URI_ENV_VAR: "https://my-uri", + _HTTP_API_KEY_ENV_VAR: "my-api-key", + }, + ): + config = JupyterHttpConfiguration() + assert config.server_uri == "https://my-uri" + assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.cert_path is None From 5d25fafd7a6d53b10f575955d84fd5f0ceec0061 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 22:35:33 +0300 Subject: [PATCH 07/16] Fix black formatting in JupyterHttpConfiguration --- nisystemlink/clients/core/_jupyter_http_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 081361d7..b4942653 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -30,7 +30,7 @@ def __init__(self) -> None: # `HttpConfigurations` are no longer readable, creating client objects from this library, from SLS notebooks, # will default to using this `JupyterHttpConfiguration`. However, this does not set a `cert_path`, so HTTPS # requests will fail. - # When running SystemLink Server notebooks (Windows), we will therefore pass the Web Server CA certificate's path as the + # When running SystemLink Server notebooks (Windows), we will therefore pass the Web Server CA certificate's path as the # `cert_path`, if the file exists. This will allow clients created with the default configuration to use this CA certificate. # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid `cert_path` # will lead to errors. From 9ac201eb2eff1e6a6d2640734d53040eb7bc1c61 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 22:46:03 +0300 Subject: [PATCH 08/16] Fix linting for test_jupyter_http_configuration.py --- tests/core/test_jupyter_http_configuration.py | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/tests/core/test_jupyter_http_configuration.py b/tests/core/test_jupyter_http_configuration.py index 717b3aa7..1f128513 100644 --- a/tests/core/test_jupyter_http_configuration.py +++ b/tests/core/test_jupyter_http_configuration.py @@ -1,11 +1,15 @@ +import os +from unittest.mock import patch + from nisystemlink.clients.core import JupyterHttpConfiguration -from unittest.mock import patch -import os _HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" _HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" -_SYSTEMLINK_SERVER_CERTIFICATE_PATH = r"C:\ProgramData\National Instruments\Skyline\Certificates\http-server\http-server.cer" +_SYSTEMLINK_SERVER_CERTIFICATE_PATH = ( + r"C:\ProgramData\National Instruments\Skyline" + r"\Certificates\http-server\http-server.cer" +) _SYSTEM_LINK_API_KEY_HEADER = "x-ni-api-key" @@ -50,26 +54,6 @@ def mock_exists(path): assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" assert config.cert_path is None - def test__cert_file_does_not_exist_on_windows__cert_path_is_not_passed(self): - def mock_exists(path): - if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: - return False - return True - - with patch("sys.platform", "win32"): - with patch("os.path.exists", side_effect=mock_exists): - with patch.dict( - os.environ, - { - _HTTP_URI_ENV_VAR: "https://my-uri", - _HTTP_API_KEY_ENV_VAR: "my-api-key", - }, - ): - config = JupyterHttpConfiguration() - assert config.server_uri == "https://my-uri" - assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" - assert config.cert_path is None - def test__cert_file_exists_on_linux__cert_path_is_not_passed(self): def mock_exists(path): if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: From 6c3e6bef321050de5ef39ae37ecd31f5df2ae4e0 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 22:46:54 +0300 Subject: [PATCH 09/16] Fix linting for _jupyter_http_configuration.py --- .../core/_jupyter_http_configuration.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index b4942653..f7f8db05 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -13,7 +13,10 @@ class JupyterHttpConfiguration(core.HttpConfiguration): _HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" _HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" - _SYSTEMLINK_SERVER_CERT_PATH = r"C:\ProgramData\National Instruments\Skyline\Certificates\http-server\http-server.cer" + _SYSTEMLINK_SERVER_CERT_PATH = ( + r"C:\ProgramData\National Instruments\Skyline" + r"\Certificates\http-server\http-server.cer" + ) def __init__(self) -> None: """Initialize a configuration for SystemLink using API key-based @@ -25,15 +28,16 @@ def __init__(self) -> None: http_uri = os.environ[self._HTTP_URI_ENV_VAR] api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] - # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder is - # no longer granted for notebooks running under the JupyterHub/NotebookExecution services. Since config files under - # `HttpConfigurations` are no longer readable, creating client objects from this library, from SLS notebooks, - # will default to using this `JupyterHttpConfiguration`. However, this does not set a `cert_path`, so HTTPS - # requests will fail. - # When running SystemLink Server notebooks (Windows), we will therefore pass the Web Server CA certificate's path as the - # `cert_path`, if the file exists. This will allow clients created with the default configuration to use this CA certificate. - # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid `cert_path` - # will lead to errors. + # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder + # is no longer granted for notebooks running under the JupyterHub/NotebookExecution services. Since config + # files under `HttpConfigurations` are no longer readable, creating client objects from this library, from SLS + # notebooks, will default to using this `JupyterHttpConfiguration`. However, this does not set a `cert_path`, + # so HTTPS requests will fail. + # When running SystemLink Server notebooks (Windows), we will therefore pass the Web Server CA certificate's + # path as the `cert_path`, if the file exists. This will allow clients created with the default configuration + # to use this CA certificate. + # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid + # `cert_path` will lead to errors. if sys.platform.startswith("win") and os.path.exists( self._SYSTEMLINK_SERVER_CERT_PATH ): From cdab6d8750bc96c6b5c6d7973f1df60d6aa525e4 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 22:59:07 +0300 Subject: [PATCH 10/16] Pass a pathlib.Path as cert_path argument to HttpConfiguration, instead of a string, per AI code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- nisystemlink/clients/core/_jupyter_http_configuration.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index f7f8db05..c6a7dc67 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -41,8 +41,12 @@ def __init__(self) -> None: if sys.platform.startswith("win") and os.path.exists( self._SYSTEMLINK_SERVER_CERT_PATH ): + import pathlib + super().__init__( - http_uri, api_key, cert_path=self._SYSTEMLINK_SERVER_CERT_PATH + http_uri, + api_key, + cert_path=pathlib.Path(self._SYSTEMLINK_SERVER_CERT_PATH), ) else: super().__init__(http_uri, api_key) From 24dce3342836787f31ab00f7fa3d162e3e207b22 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 23:17:24 +0300 Subject: [PATCH 11/16] Use PathConstants to retrieve ProgramData directory, per code review --- .../clients/core/_jupyter_http_configuration.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index c6a7dc67..7796c2c7 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -6,6 +6,7 @@ import sys from nisystemlink.clients import core +from nisystemlink.clients.core._internal._path_constants import PathConstants class JupyterHttpConfiguration(core.HttpConfiguration): @@ -14,8 +15,10 @@ class JupyterHttpConfiguration(core.HttpConfiguration): _HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" _HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" _SYSTEMLINK_SERVER_CERT_PATH = ( - r"C:\ProgramData\National Instruments\Skyline" - r"\Certificates\http-server\http-server.cer" + PathConstants.application_data_directory + / "Certificates" + / "http-server" + / "http-server.cer" ) def __init__(self) -> None: @@ -29,7 +32,7 @@ def __init__(self) -> None: api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder - # is no longer granted for notebooks running under the JupyterHub/NotebookExecution services. Since config + # is no longer granted for notebooks running under the JupyterHub/NotebookExecution services. Since config # files under `HttpConfigurations` are no longer readable, creating client objects from this library, from SLS # notebooks, will default to using this `JupyterHttpConfiguration`. However, this does not set a `cert_path`, # so HTTPS requests will fail. @@ -41,12 +44,8 @@ def __init__(self) -> None: if sys.platform.startswith("win") and os.path.exists( self._SYSTEMLINK_SERVER_CERT_PATH ): - import pathlib - super().__init__( - http_uri, - api_key, - cert_path=pathlib.Path(self._SYSTEMLINK_SERVER_CERT_PATH), + http_uri, api_key, cert_path=self._SYSTEMLINK_SERVER_CERT_PATH ) else: super().__init__(http_uri, api_key) From 133239828e2b3189da0756307162d2eb9174e329 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 23:20:54 +0300 Subject: [PATCH 12/16] Hardcode expected cert path in test --- tests/core/test_jupyter_http_configuration.py | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/core/test_jupyter_http_configuration.py b/tests/core/test_jupyter_http_configuration.py index 1f128513..ae07a7b3 100644 --- a/tests/core/test_jupyter_http_configuration.py +++ b/tests/core/test_jupyter_http_configuration.py @@ -1,4 +1,5 @@ import os +import pathlib from unittest.mock import patch from nisystemlink.clients.core import JupyterHttpConfiguration @@ -6,17 +7,19 @@ _HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" _HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" -_SYSTEMLINK_SERVER_CERTIFICATE_PATH = ( - r"C:\ProgramData\National Instruments\Skyline" - r"\Certificates\http-server\http-server.cer" +_SYSTEMLINK_SERVER_CERT_PATH = ( + pathlib.Path(r"C:\ProgramData\National Instruments\Skyline") + / "Certificates" + / "http-server" + / "http-server.cer" ) -_SYSTEM_LINK_API_KEY_HEADER = "x-ni-api-key" +_SYSTEMLINK_API_KEY_HEADER = "x-ni-api-key" class TestJupyterHttpConfiguration: def test__cert_file_exists_on_windows__cert_path_is_passed(self): def mock_exists(path): - if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + if path == _SYSTEMLINK_SERVER_CERT_PATH: return True return False @@ -31,12 +34,12 @@ def mock_exists(path): ): config = JupyterHttpConfiguration() assert config.server_uri == "https://my-uri" - assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" - assert config.cert_path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH + assert config.api_keys[_SYSTEMLINK_API_KEY_HEADER] == "my-api-key" + assert config.cert_path == _SYSTEMLINK_SERVER_CERT_PATH def test__cert_file_does_not_exist_on_windows__cert_path_is_not_passed(self): def mock_exists(path): - if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + if path == _SYSTEMLINK_SERVER_CERT_PATH: return False return True @@ -51,12 +54,12 @@ def mock_exists(path): ): config = JupyterHttpConfiguration() assert config.server_uri == "https://my-uri" - assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.api_keys[_SYSTEMLINK_API_KEY_HEADER] == "my-api-key" assert config.cert_path is None def test__cert_file_exists_on_linux__cert_path_is_not_passed(self): def mock_exists(path): - if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + if path == _SYSTEMLINK_SERVER_CERT_PATH: return True return False @@ -71,12 +74,12 @@ def mock_exists(path): ): config = JupyterHttpConfiguration() assert config.server_uri == "https://my-uri" - assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.api_keys[_SYSTEMLINK_API_KEY_HEADER] == "my-api-key" assert config.cert_path is None def test__cert_file_does_not_exist_on_linux__cert_path_is_not_passed(self): def mock_exists(path): - if path == _SYSTEMLINK_SERVER_CERTIFICATE_PATH: + if path == _SYSTEMLINK_SERVER_CERT_PATH: return False return True @@ -91,5 +94,5 @@ def mock_exists(path): ): config = JupyterHttpConfiguration() assert config.server_uri == "https://my-uri" - assert config.api_keys[_SYSTEM_LINK_API_KEY_HEADER] == "my-api-key" + assert config.api_keys[_SYSTEMLINK_API_KEY_HEADER] == "my-api-key" assert config.cert_path is None From e40c272cdced9beecdeae7e36ba3a60a533b5677 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 23:52:13 +0300 Subject: [PATCH 13/16] Compute the SLS cert path in __init__ to ease mocking --- .../clients/core/_jupyter_http_configuration.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 7796c2c7..929481a7 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -14,12 +14,6 @@ class JupyterHttpConfiguration(core.HttpConfiguration): _HTTP_URI_ENV_VAR = "SYSTEMLINK_HTTP_URI" _HTTP_API_KEY_ENV_VAR = "SYSTEMLINK_API_KEY" - _SYSTEMLINK_SERVER_CERT_PATH = ( - PathConstants.application_data_directory - / "Certificates" - / "http-server" - / "http-server.cer" - ) def __init__(self) -> None: """Initialize a configuration for SystemLink using API key-based @@ -30,6 +24,10 @@ def __init__(self) -> None: """ http_uri = os.environ[self._HTTP_URI_ENV_VAR] api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] + systemlink_server_cert_path = ( + PathConstants.application_data_directory + / "Certificates" / "http-server" / "http-server.cer" + ) # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder # is no longer granted for notebooks running under the JupyterHub/NotebookExecution services. Since config @@ -41,11 +39,9 @@ def __init__(self) -> None: # to use this CA certificate. # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid # `cert_path` will lead to errors. - if sys.platform.startswith("win") and os.path.exists( - self._SYSTEMLINK_SERVER_CERT_PATH - ): + if sys.platform.startswith("win") and os.path.exists(systemlink_server_cert_path): super().__init__( - http_uri, api_key, cert_path=self._SYSTEMLINK_SERVER_CERT_PATH + http_uri, api_key, cert_path=systemlink_server_cert_path ) else: super().__init__(http_uri, api_key) From 590875f92324a586e3dbbeb08624f0f6d83eb030 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Wed, 15 Jul 2026 23:53:17 +0300 Subject: [PATCH 14/16] [Tests] Patch the ProgramData path from PathConstants so that it returns the proper Windows path when running the tests on Linux --- tests/core/test_jupyter_http_configuration.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/core/test_jupyter_http_configuration.py b/tests/core/test_jupyter_http_configuration.py index ae07a7b3..57325689 100644 --- a/tests/core/test_jupyter_http_configuration.py +++ b/tests/core/test_jupyter_http_configuration.py @@ -17,6 +17,12 @@ class TestJupyterHttpConfiguration: + # Patch PathConstants._application_data_directory so that we have the proper Windows path + # when running the tests on Linux. + @patch( + "nisystemlink.clients.core._internal._path_constants.PathConstants._application_data_directory", + pathlib.Path(r"C:\ProgramData\National Instruments\Skyline"), + ) def test__cert_file_exists_on_windows__cert_path_is_passed(self): def mock_exists(path): if path == _SYSTEMLINK_SERVER_CERT_PATH: From 00bfe360e7f1ed17b8d932f1e00fe5083d035a66 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Thu, 16 Jul 2026 00:01:26 +0300 Subject: [PATCH 15/16] Patch PathConstants._application_data_directory for the other Windows test --- tests/core/test_jupyter_http_configuration.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/core/test_jupyter_http_configuration.py b/tests/core/test_jupyter_http_configuration.py index 57325689..53edfa0b 100644 --- a/tests/core/test_jupyter_http_configuration.py +++ b/tests/core/test_jupyter_http_configuration.py @@ -43,6 +43,12 @@ def mock_exists(path): assert config.api_keys[_SYSTEMLINK_API_KEY_HEADER] == "my-api-key" assert config.cert_path == _SYSTEMLINK_SERVER_CERT_PATH + # Patch PathConstants._application_data_directory so that we have the proper Windows path + # when running the tests on Linux. + @patch( + "nisystemlink.clients.core._internal._path_constants.PathConstants._application_data_directory", + pathlib.Path(r"C:\ProgramData\National Instruments\Skyline"), + ) def test__cert_file_does_not_exist_on_windows__cert_path_is_not_passed(self): def mock_exists(path): if path == _SYSTEMLINK_SERVER_CERT_PATH: From 742211ea8e07fa65ad43cfdc19f217da469c7821 Mon Sep 17 00:00:00 2001 From: Alexandru Dan Duna Date: Thu, 16 Jul 2026 00:08:34 +0300 Subject: [PATCH 16/16] Fix black linting --- .../clients/core/_jupyter_http_configuration.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/nisystemlink/clients/core/_jupyter_http_configuration.py b/nisystemlink/clients/core/_jupyter_http_configuration.py index 929481a7..cb3936c0 100644 --- a/nisystemlink/clients/core/_jupyter_http_configuration.py +++ b/nisystemlink/clients/core/_jupyter_http_configuration.py @@ -26,7 +26,9 @@ def __init__(self) -> None: api_key = os.environ[self._HTTP_API_KEY_ENV_VAR] systemlink_server_cert_path = ( PathConstants.application_data_directory - / "Certificates" / "http-server" / "http-server.cer" + / "Certificates" + / "http-server" + / "http-server.cer" ) # SystemLink Server 26Q3 restricts notebook executions (by default). Access to the `HttpConfigurations` folder @@ -39,9 +41,9 @@ def __init__(self) -> None: # to use this CA certificate. # If the file does not exist (when the Web Server is configured in HTTP mode), do not pass it; an invalid # `cert_path` will lead to errors. - if sys.platform.startswith("win") and os.path.exists(systemlink_server_cert_path): - super().__init__( - http_uri, api_key, cert_path=systemlink_server_cert_path - ) + if sys.platform.startswith("win") and os.path.exists( + systemlink_server_cert_path + ): + super().__init__(http_uri, api_key, cert_path=systemlink_server_cert_path) else: super().__init__(http_uri, api_key)