utils.py 3.9 KB

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