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)