From 383655dce0c09b9bc4a4c9c1409331517113711f Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Thu, 16 Sep 2021 10:22:10 -0700 Subject: [PATCH] Address missing local-provisioner scenario --- jupyter_client/provisioning/factory.py | 40 +++++++++++++++++++---- jupyter_client/tests/test_provisioning.py | 2 +- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/jupyter_client/provisioning/factory.py b/jupyter_client/provisioning/factory.py index 212e03625..c85009cd9 100644 --- a/jupyter_client/provisioning/factory.py +++ b/jupyter_client/provisioning/factory.py @@ -1,7 +1,9 @@ """Kernel Provisioner Classes""" # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -import os +import glob +from os import getenv +from os import path from typing import Any from typing import Dict from typing import List @@ -44,7 +46,7 @@ class KernelProvisionerFactory(SingletonConfigurable): @default('default_provisioner_name') def default_provisioner_name_default(self): - return os.getenv(self.default_provisioner_name_env, "local-provisioner") + return getenv(self.default_provisioner_name_env, "local-provisioner") def __init__(self, **kwargs) -> None: super().__init__(**kwargs) @@ -115,7 +117,7 @@ def _check_availability(self, provisioner_name: str) -> bool: is_available = True if provisioner_name not in self.provisioners: try: - ep = KernelProvisionerFactory._get_provisioner(provisioner_name) + ep = self._get_provisioner(provisioner_name) self.provisioners[provisioner_name] = ep # Update cache except NoSuchEntryPoint: is_available = False @@ -167,7 +169,33 @@ def _get_all_provisioners() -> List[EntryPoint]: """Wrapper around entrypoints.get_group_all() - primarily to facilitate testing.""" return get_group_all(KernelProvisionerFactory.GROUP_NAME) - @staticmethod - def _get_provisioner(name: str) -> EntryPoint: + def _get_provisioner(self, name: str) -> EntryPoint: """Wrapper around entrypoints.get_single() - primarily to facilitate testing.""" - return get_single(KernelProvisionerFactory.GROUP_NAME, name) + try: + ep = get_single(KernelProvisionerFactory.GROUP_NAME, name) + except NoSuchEntryPoint: + # Check if the entrypoint name is 'local-provisioner'. Although this should never + # happen, we have seen cases where the previous distribution of jupyter_client has + # remained which doesn't include kernel-provisioner entrypoints (so 'local-provisioner' + # is deemed not found even though its definition is in THIS package). In such cass, + # the entrypoints package uses what it first finds - which is the older distribution + # resulting in a violation of a supposed invariant condition. To address this scenario, + # we will log a warning message indicating this situation, then build the entrypoint + # instance ourselves - since we have that information. + if name == 'local-provisioner': + distros = glob.glob(f"{path.dirname(path.dirname(__file__))}-*") + self.log.warn( + f"Kernel Provisioning: The 'local-provisioner' is not found. This is likely " + f"due to the presence of multiple jupyter_client distributions and a previous " + f"distribution is being used as the source for entrypoints - which does not " + f"include 'local-provisioner'. That distribution should be removed such that " + f"only the version-appropriate distribution remains (version >= 7). Until " + f"then, a 'local-provisioner' entrypoint will be automatically constructed " + f"and used.\nThe candidate distribution locations are: {distros}" + ) + ep = EntryPoint( + 'local-provisioner', 'jupyter_client.provisioning', 'LocalProvisioner' + ) + else: + raise + return ep diff --git a/jupyter_client/tests/test_provisioning.py b/jupyter_client/tests/test_provisioning.py index 3aa3db0e2..f6db54c01 100644 --- a/jupyter_client/tests/test_provisioning.py +++ b/jupyter_client/tests/test_provisioning.py @@ -210,7 +210,7 @@ def mock_get_all_provisioners() -> List[EntryPoint]: return result -def mock_get_provisioner(name) -> EntryPoint: +def mock_get_provisioner(factory, name) -> EntryPoint: if name == 'new-test-provisioner': return EntryPoint( 'new-test-provisioner', 'jupyter_client.tests.test_provisioning', 'NewTestProvisioner'