|
1 | 1 | """Kernel Provisioner Classes"""
|
2 | 2 | # Copyright (c) Jupyter Development Team.
|
3 | 3 | # Distributed under the terms of the Modified BSD License.
|
4 |
| -import os |
| 4 | +import glob |
| 5 | +from os import getenv |
| 6 | +from os import path |
5 | 7 | from typing import Any
|
6 | 8 | from typing import Dict
|
7 | 9 | from typing import List
|
@@ -44,7 +46,7 @@ class KernelProvisionerFactory(SingletonConfigurable):
|
44 | 46 |
|
45 | 47 | @default('default_provisioner_name')
|
46 | 48 | def default_provisioner_name_default(self):
|
47 |
| - return os.getenv(self.default_provisioner_name_env, "local-provisioner") |
| 49 | + return getenv(self.default_provisioner_name_env, "local-provisioner") |
48 | 50 |
|
49 | 51 | def __init__(self, **kwargs) -> None:
|
50 | 52 | super().__init__(**kwargs)
|
@@ -115,7 +117,7 @@ def _check_availability(self, provisioner_name: str) -> bool:
|
115 | 117 | is_available = True
|
116 | 118 | if provisioner_name not in self.provisioners:
|
117 | 119 | try:
|
118 |
| - ep = KernelProvisionerFactory._get_provisioner(provisioner_name) |
| 120 | + ep = self._get_provisioner(provisioner_name) |
119 | 121 | self.provisioners[provisioner_name] = ep # Update cache
|
120 | 122 | except NoSuchEntryPoint:
|
121 | 123 | is_available = False
|
@@ -167,7 +169,33 @@ def _get_all_provisioners() -> List[EntryPoint]:
|
167 | 169 | """Wrapper around entrypoints.get_group_all() - primarily to facilitate testing."""
|
168 | 170 | return get_group_all(KernelProvisionerFactory.GROUP_NAME)
|
169 | 171 |
|
170 |
| - @staticmethod |
171 |
| - def _get_provisioner(name: str) -> EntryPoint: |
| 172 | + def _get_provisioner(self, name: str) -> EntryPoint: |
172 | 173 | """Wrapper around entrypoints.get_single() - primarily to facilitate testing."""
|
173 |
| - return get_single(KernelProvisionerFactory.GROUP_NAME, name) |
| 174 | + try: |
| 175 | + ep = get_single(KernelProvisionerFactory.GROUP_NAME, name) |
| 176 | + except NoSuchEntryPoint: |
| 177 | + # Check if the entrypoint name is 'local-provisioner'. Although this should never |
| 178 | + # happen, we have seen cases where the previous distribution of jupyter_client has |
| 179 | + # remained which doesn't include kernel-provisioner entrypoints (so 'local-provisioner' |
| 180 | + # is deemed not found even though its definition is in THIS package). In such cass, |
| 181 | + # the entrypoints package uses what it first finds - which is the older distribution |
| 182 | + # resulting in a violation of a supposed invariant condition. To address this scenario, |
| 183 | + # we will log a warning message indicating this situation, then build the entrypoint |
| 184 | + # instance ourselves - since we have that information. |
| 185 | + if name == 'local-provisioner': |
| 186 | + distros = glob.glob(f"{path.dirname(path.dirname(__file__))}-*") |
| 187 | + self.log.warn( |
| 188 | + f"Kernel Provisioning: The 'local-provisioner' is not found. This is likely " |
| 189 | + f"due to the presence of multiple jupyter_client distributions and a previous " |
| 190 | + f"distribution is being used as the source for entrypoints - which does not " |
| 191 | + f"include 'local-provisioner'. That distribution should be removed such that " |
| 192 | + f"only the version-appropriate distribution remains (version >= 7). Until " |
| 193 | + f"then, a 'local-provisioner' entrypoint will be automatically constructed " |
| 194 | + f"and used.\nThe candidate distribution locations are: {distros}" |
| 195 | + ) |
| 196 | + ep = EntryPoint( |
| 197 | + 'local-provisioner', 'jupyter_client.provisioning', 'LocalProvisioner' |
| 198 | + ) |
| 199 | + else: |
| 200 | + raise |
| 201 | + return ep |
0 commit comments