File size: 3,982 Bytes
d1ceb73 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
"""Extension utilities."""
import importlib
import time
import warnings
class ExtensionLoadingError(Exception):
"""An extension loading error."""
class ExtensionMetadataError(Exception):
"""An extension metadata error."""
class ExtensionModuleNotFound(Exception):
"""An extension module not found error."""
class NotAnExtensionApp(Exception):
"""An error raised when a module is not an extension."""
def get_loader(obj, logger=None):
"""Looks for _load_jupyter_server_extension as an attribute
of the object or module.
Adds backwards compatibility for old function name missing the
underscore prefix.
"""
try:
return obj._load_jupyter_server_extension
except AttributeError:
pass
try:
func = obj.load_jupyter_server_extension
except AttributeError:
msg = "_load_jupyter_server_extension function was not found."
raise ExtensionLoadingError(msg) from None
warnings.warn(
"A `_load_jupyter_server_extension` function was not "
f"found in {obj!s}. Instead, a `load_jupyter_server_extension` "
"function was found and will be used for now. This function "
"name will be deprecated in future releases "
"of Jupyter Server.",
DeprecationWarning,
stacklevel=2,
)
return func
def get_metadata(package_name, logger=None):
"""Find the extension metadata from an extension package.
This looks for a `_jupyter_server_extension_points` function
that returns metadata about all extension points within a Jupyter
Server Extension package.
If it doesn't exist, return a basic metadata packet given
the module name.
"""
start_time = time.perf_counter()
module = importlib.import_module(package_name)
end_time = time.perf_counter()
duration = end_time - start_time
# Sometimes packages can take a *while* to import, so we report how long
# each module took to import. This makes it much easier for users to report
# slow loading modules upstream, as slow loading modules will block server startup
if logger:
log = logger.info if duration > 0.1 else logger.debug
log(f"Extension package {package_name} took {duration:.4f}s to import")
try:
return module, module._jupyter_server_extension_points()
except AttributeError:
pass
# For backwards compatibility, we temporarily allow
# _jupyter_server_extension_paths. We will remove in
# a later release of Jupyter Server.
try:
extension_points = module._jupyter_server_extension_paths()
if logger:
logger.warning(
"A `_jupyter_server_extension_points` function was not "
f"found in {package_name}. Instead, a `_jupyter_server_extension_paths` "
"function was found and will be used for now. This function "
"name will be deprecated in future releases "
"of Jupyter Server."
)
return module, extension_points
except AttributeError:
pass
# Dynamically create metadata if the package doesn't
# provide it.
if logger:
logger.debug(
"A `_jupyter_server_extension_points` function was "
f"not found in {package_name}, so Jupyter Server will look "
"for extension points in the extension pacakge's "
"root."
)
return module, [{"module": package_name, "name": package_name}]
def validate_extension(name):
"""Raises an exception is the extension is missing a needed
hook or metadata field.
An extension is valid if:
1) name is an importable Python package.
1) the package has a _jupyter_server_extension_points function
2) each extension path has a _load_jupyter_server_extension function
If this works, nothing should happen.
"""
from .manager import ExtensionPackage
return ExtensionPackage(name=name)
|