Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExtensionApp: change extension_name to name. #232

Merged
merged 3 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions docs/source/developers/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ An ExtensionApp:

- has traits.
- is configurable (from file or CLI)
- has a name (see the ``extension_name`` trait).
- has an entrypoint, ``jupyter <extension_name>``.
- can serve static content from the ``/static/<extension_name>/`` endpoint.
- has a name (see the ``name`` trait).
- has an entrypoint, ``jupyter <name>``.
- can serve static content from the ``/static/<name>/`` endpoint.
- can add new endpoints to the Jupyter Server.

The basic structure of an ExtensionApp is shown below:
Expand All @@ -124,7 +124,7 @@ The basic structure of an ExtensionApp is shown below:
class MyExtensionApp(ExtensionApp):

# -------------- Required traits --------------
extension_name = "myextension"
name = "myextension"
extension_url = "/myextension"
load_other_extensions = True

Expand Down Expand Up @@ -163,7 +163,7 @@ Methods

Properties

* ``extension_name``: the name of the extension
* ``name``: the name of the extension
* ``extension_url``: the default url for this extension—i.e. the landing page for this extension when launched from the CLI.
* ``load_other_extensions``: a boolean enabling/disabling other extensions when launching this extension directly.

Expand All @@ -174,8 +174,8 @@ Properties

* ``config``: the ExtensionApp's config object.
* ``server_config``: the ServerApp's config object.
* ``extension_name``: the name of the extension to which this handler is linked.
* ``static_url()``: a method that returns the url to static files (prefixed with ``/static/<extension_name>``).
* ``name``: the name of the extension to which this handler is linked.
* ``static_url()``: a method that returns the url to static files (prefixed with ``/static/<name>``).

Jupyter Server provides a convenient mixin class for adding these properties to any ``JupyterHandler``. For example, the basic server extension handler in the section above becomes:

Expand All @@ -202,7 +202,7 @@ Jinja templating from frontend extensions

Many Jupyter frontend applications use Jinja for basic HTML templating. Since this is common enough, Jupyter Server provides some extra mixin that integrate Jinja with Jupyter server extensions.

Use ``ExtensionAppJinjaMixin`` to automatically add a Jinja templating environment to an ``ExtensionApp``. This adds a ``<extension_name>_jinja2_env`` setting to Tornado Web Server's settings that will be used by request handlers.
Use ``ExtensionAppJinjaMixin`` to automatically add a Jinja templating environment to an ``ExtensionApp``. This adds a ``<name>_jinja2_env`` setting to Tornado Web Server's settings that will be used by request handlers.

.. code-block:: python

Expand Down
2 changes: 1 addition & 1 deletion docs/source/operators/configuring-extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Jupyter Server looks for an extension's config file in a set of specific paths.
Extension config from file
--------------------------

Jupyter Server expects the file to be named after the extension's name like so: ``jupyter_{extension_name}_config``. For example, the Jupyter Notebook's config file is ``jupyter_notebook_config``.
Jupyter Server expects the file to be named after the extension's name like so: ``jupyter_{name}_config``. For example, the Jupyter Notebook's config file is ``jupyter_notebook_config``.

Configuration files can be Python or JSON files.

Expand Down
12 changes: 6 additions & 6 deletions examples/simple/simple_ext1/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class SimpleApp1(ExtensionAppJinjaMixin, ExtensionApp):

# The name of the extension.
extension_name = "simple_ext1"
name = "simple_ext1"

# The url that your extension will serve its homepage.
extension_url = '/simple_ext1/default'
Expand Down Expand Up @@ -45,11 +45,11 @@ class SimpleApp1(ExtensionAppJinjaMixin, ExtensionApp):

def initialize_handlers(self):
self.handlers.extend([
(r'/{}/default'.format(self.extension_name), DefaultHandler),
(r'/{}/params/(.+)$'.format(self.extension_name), ParameterHandler),
(r'/{}/template1/(.*)$'.format(self.extension_name), TemplateHandler),
(r'/{}/redirect'.format(self.extension_name), RedirectHandler),
(r'/{}/typescript/?'.format(self.extension_name), TypescriptHandler),
(r'/{}/default'.format(self.name), DefaultHandler),
(r'/{}/params/(.+)$'.format(self.name), ParameterHandler),
(r'/{}/template1/(.*)$'.format(self.name), TemplateHandler),
(r'/{}/redirect'.format(self.name), RedirectHandler),
(r'/{}/typescript/?'.format(self.name), TypescriptHandler),
(r'/{}/(.*)', ErrorHandler)
])

Expand Down
10 changes: 5 additions & 5 deletions examples/simple/simple_ext1/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
class DefaultHandler(ExtensionHandlerMixin, JupyterHandler):
def get(self):
# The name of the extension to which this handler is linked.
self.log.info("Extension Name in {} Default Handler: {}".format(self.extension_name, self.extension_name))
# A method for getting the url to static files (prefixed with /static/<extension_name>).
self.log.info("Extension Name in {} Default Handler: {}".format(self.name, self.name))
# A method for getting the url to static files (prefixed with /static/<name>).
self.log.info("Static URL for / in simple_ext1 Default Handler:".format(self.static_url(path='/')))
self.write('<h1>Hello Simple 1 - I am the default...</h1>')
self.write('Config in {} Default Handler: {}'.format(self.extension_name, self.config))
self.write('Config in {} Default Handler: {}'.format(self.name, self.config))

class RedirectHandler(ExtensionHandlerMixin, JupyterHandler):
def get(self):
self.redirect("/static/{}/favicon.ico".format(self.extension_name))
self.redirect("/static/{}/favicon.ico".format(self.name))

class ParameterHandler(ExtensionHandlerMixin, JupyterHandler):
class ParameterHandler(ExtensionHandlerMixin, JupyterHandler):
def get(self, matched_part=None, *args, **kwargs):
var1 = self.get_argument('var1', default=None)
components = [x for x in self.request.path.split("/") if x]
Expand Down
2 changes: 1 addition & 1 deletion examples/simple/simple_ext11/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SimpleApp11(SimpleApp1):
})

# The name of the extension.
extension_name = "simple_ext11"
name = "simple_ext11"

# Te url that your extension will serve its homepage.
extension_url = '/simple_ext11/default'
Expand Down
2 changes: 1 addition & 1 deletion examples/simple/simple_ext2/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class SimpleApp2(ExtensionAppJinjaMixin, ExtensionApp):

# The name of the extension.
extension_name = "simple_ext2"
name = "simple_ext2"

# Te url that your extension will serve its homepage.
extension_url = '/simple_ext2'
Expand Down
63 changes: 21 additions & 42 deletions jupyter_server/extension/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def _prepare_templates(self):
# Add templates to web app settings if extension has templates.
if len(self.template_paths) > 0:
self.settings.update({
"{}_template_paths".format(self.extension_name): self.template_paths
"{}_template_paths".format(self.name): self.template_paths
})

# Create a jinja environment for logging html templates.
Expand All @@ -111,7 +111,7 @@ def _prepare_templates(self):
# Add the jinja2 environment for this extension to the tornado settings.
self.settings.update(
{
"{}_jinja2_env".format(self.extension_name): self.jinja2_env
"{}_jinja2_env".format(self.name): self.jinja2_env
}
)

Expand Down Expand Up @@ -144,32 +144,11 @@ class method. This method can be set as a entry_point in
# side-by-side when launched directly.
load_other_extensions = True

# Name of the extension
extension_name = Unicode(
help="Name of extension."
)

@default('extension_name')
def _extension_name_default(self):
try:
return self.name
except AttributeError:
raise ValueError("The extension must be given a `name`.")

INVALID_EXTENSION_NAME_CHARS = [' ', '.', '+', '/']

@validate('extension_name')
def _validate_extension_name(self, proposal):
value = proposal['value']
if isinstance(value, str):
# Validate that extension_name doesn't contain any invalid characters.
for c in ExtensionApp.INVALID_EXTENSION_NAME_CHARS:
if c in value:
raise ValueError("Extension name '{name}' cannot contain any of the following characters: "
"{invalid_chars}.".
format(name=value, invalid_chars=ExtensionApp.INVALID_EXTENSION_NAME_CHARS))
return value
raise ValueError("Extension name must be a string, found {type}.".format(type=type(value)))
# The extension name used to name the jupyter config
# file, jupyter_{name}_config.
# This should also match the jupyter subcommand used to launch
# this extension from the CLI, e.g. `jupyter {name}`.
name = None

# Extension URL sets the default landing page for this extension.
extension_url = "/"
Expand All @@ -181,8 +160,8 @@ def _validate_extension_name(self, proposal):

@property
def static_url_prefix(self):
return "/static/{extension_name}/".format(
extension_name=self.extension_name)
return "/static/{name}/".format(
name=self.name)

static_paths = List(Unicode(),
help="""paths to search for serving static files.
Expand Down Expand Up @@ -216,10 +195,9 @@ def static_url_prefix(self):

def _config_file_name_default(self):
"""The default config file name."""
if not self.extension_name:
if not self.name:
return ''
return 'jupyter_{}_config'.format(self.extension_name.replace('-','_'))

return 'jupyter_{}_config'.format(self.name.replace('-','_'))

def initialize_settings(self):
"""Override this method to add handling of settings."""
Expand All @@ -235,11 +213,11 @@ def initialize_templates(self):

def _prepare_config(self):
"""Builds a Config object from the extension's traits and passes
the object to the webapp's settings as `<extension_name>_config`.
the object to the webapp's settings as `<name>_config`.
"""
traits = self.class_own_traits().keys()
self.extension_config = Config({t: getattr(self, t) for t in traits})
self.settings['{}_config'.format(self.extension_name)] = self.extension_config
self.settings['{}_config'.format(self.name)] = self.extension_config

def _prepare_settings(self):
# Make webapp settings accessible to initialize_settings method
Expand All @@ -248,8 +226,8 @@ def _prepare_settings(self):

# Add static and template paths to settings.
self.settings.update({
"{}_static_paths".format(self.extension_name): self.static_paths,
"{}".format(self.extension_name): self
"{}_static_paths".format(self.name): self.static_paths,
"{}".format(self.name): self
})

# Get setting defined by subclass using initialize_settings method.
Expand All @@ -274,7 +252,8 @@ def _prepare_handlers(self):
# Get handler kwargs, if given
kwargs = {}
if issubclass(handler, ExtensionHandlerMixin):
kwargs['extension_name'] = self.extension_name
kwargs['name'] = self.name

try:
kwargs.update(handler_items[2])
except IndexError:
Expand Down Expand Up @@ -302,7 +281,7 @@ def _prepare_templates(self):
# Add templates to web app settings if extension has templates.
if len(self.template_paths) > 0:
self.settings.update({
"{}_template_paths".format(self.extension_name): self.template_paths
"{}_template_paths".format(self.name): self.template_paths
})
self.initialize_templates()

Expand All @@ -319,7 +298,7 @@ def initialize_server(cls, argv=[], load_other_extensions=True, **kwargs):
# initializes it.
config = Config({
"ServerApp": {
"jpserver_extensions": {cls.extension_name: True},
"jpserver_extensions": {cls.name: True},
"open_browser": cls.open_browser,
"default_url": cls.extension_url
}
Expand Down Expand Up @@ -402,7 +381,7 @@ def _load_jupyter_server_extension(cls, serverapp):
"""
try:
# Get loaded extension from serverapp.
extension = serverapp._enabled_extensions[cls.extension_name]
extension = serverapp._enabled_extensions[cls.name]
except KeyError:
extension = cls()
extension.link_to_serverapp(serverapp)
Expand Down Expand Up @@ -438,6 +417,6 @@ def launch_instance(cls, argv=None, **kwargs):
if not cls.load_other_extensions:
serverapp.log.info(
"{ext_name} is running without loading "
"other extensions.".format(ext_name=cls.extension_name)
"other extensions.".format(ext_name=cls.name)
)
serverapp.start()
21 changes: 10 additions & 11 deletions jupyter_server/extension/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@ class ExtensionHandlerJinjaMixin:
"""
def get_template(self, name):
"""Return the jinja template object for a given name"""
env = '{}_jinja2_env'.format(self.extension_name)
env = '{}_jinja2_env'.format(self.name)
return self.settings[env].get_template(name)


class ExtensionHandlerMixin:
"""Base class for Jupyter server extension handlers.

Subclasses can serve static files behind a namespaced
endpoint: "/static/<extension_name>/"
endpoint: "/static/<name>/"

This allows multiple extensions to serve static files under
their own namespace and avoid intercepting requests for
other extensions.
"""
def initialize(self, extension_name):
self.extension_name = extension_name
def initialize(self, name):
self.name = name

@property
def extensionapp(self):
return self.settings[self.extension_name]
return self.settings[self.name]

@property
def serverapp(self):
Expand All @@ -36,24 +36,23 @@ def serverapp(self):

@property
def config(self):
return self.settings["{}_config".format(self.extension_name)]
return self.settings["{}_config".format(self.name)]

@property
def server_config(self):
return self.settings["config"]

@property
def static_url_prefix(self):
return "/static/{extension_name}/".format(
extension_name=self.extension_name)
return "/static/{name}/".format(name=self.name)

@property
def static_path(self):
return self.settings['{}_static_paths'.format(self.extension_name)]
return self.settings['{}_static_paths'.format(self.name)]

def static_url(self, path, include_host=None, **kwargs):
"""Returns a static URL for the given relative static file path.
This method requires you set the ``{extension_name}_static_path``
This method requires you set the ``{name}_static_path``
setting in your extension (which specifies the root directory
of your static files).
This method returns a versioned url (by default appending
Expand All @@ -68,7 +67,7 @@ def static_url(self, path, include_host=None, **kwargs):
that value will be used as the default for all `static_url`
calls that do not pass ``include_host`` as a keyword argument.
"""
key = "{}_static_paths".format(self.extension_name)
key = "{}_static_paths".format(self.name)
try:
self.require_setting(key, "static_url")
except Exception as e:
Expand Down
8 changes: 4 additions & 4 deletions jupyter_server/extension/serverextension.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def _get_load_jupyter_server_extension(obj):
return func


def validate_server_extension(extension_name):
def validate_server_extension(name):
"""Validates that you can import the extension module,
gather all extension metadata, and find `load_jupyter_server_extension`
functions for each extension.
Expand All @@ -176,10 +176,10 @@ def validate_server_extension(extension_name):
"""
# If the extension does not exist, raise an exception
try:
mod, metadata = _get_server_extension_metadata(extension_name)
mod, metadata = _get_server_extension_metadata(name)
version = getattr(mod, '__version__', '')
except ImportError:
raise ExtensionValidationError('{} is not importable.'.format(extension_name))
raise ExtensionValidationError('{} is not importable.'.format(name))

try:
for item in metadata:
Expand All @@ -194,7 +194,7 @@ def validate_server_extension(extension_name):
raise AttributeError
# If the extension does not have a `load_jupyter_server_extension` function, raise exception.
except AttributeError:
raise ExtensionValidationError('Found "{}" module but cannot load it.'.format(extension_name))
raise ExtensionValidationError('Found "{}" module but cannot load it.'.format(name))
return version


Expand Down
6 changes: 3 additions & 3 deletions jupyter_server/serverapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1571,7 +1571,7 @@ def init_server_extensions(self):
app = extapp()
app.link_to_serverapp(self)
# Build a new list where we
self._enabled_extensions[app.extension_name] = app
self._enabled_extensions[app.name] = app
elif extloc:
extmod = importlib.import_module(extloc)
func = _get_load_jupyter_server_extension(extmod)
Expand Down Expand Up @@ -1603,8 +1603,8 @@ def load_server_extensions(self):
)
else:
log_msg = (
"Extension {extension_name} enabled and "
"loaded".format(extension_name=extension.extension_name)
"Extension {name} enabled and "
"loaded".format(name=extension.name)
)
# Find the extension loading function.
func = None
Expand Down
Loading