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

simple extension example #117

Merged
merged 38 commits into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d117ca7
simple extension example
echarles Oct 10, 2019
8ea9032
add typescript example
echarles Oct 11, 2019
ec3d196
force pr refresh
echarles Oct 11, 2019
de57ab2
force pr refresh
echarles Oct 11, 2019
51c3a65
2 apps
echarles Oct 12, 2019
c3f9c28
rm print static path
echarles Oct 12, 2019
08c1266
mention https://github.com/markellekelly/jupyter-server-example in th…
echarles Oct 14, 2019
7aa9263
add default redirect
echarles Oct 14, 2019
f6b8d47
migrate make to README + split simple_ext.json
echarles Oct 14, 2019
beff805
relax jupyter_server version
echarles Oct 16, 2019
d92fb55
add -f src
echarles Oct 16, 2019
7181ebb
refactor handlers and add a TODO fix default URL
echarles Oct 19, 2019
b5c3713
be more precise on returned error
echarles Oct 22, 2019
58eb525
Merge branch 'master' into example
echarles Oct 22, 2019
ea0555a
more examples related to settings and extension
echarles Oct 30, 2019
e9d1a69
extension 11 has flags and aliases
echarles Oct 31, 2019
b5f5766
default_url
echarles Oct 31, 2019
201bc07
Merge branch 'master' into example
echarles Nov 5, 2019
0a215bd
more settings example based on flags and file configs
echarles Nov 5, 2019
b462e9d
add a link to static home
echarles Nov 7, 2019
cab94b7
Merge branch 'master' into example
echarles Nov 16, 2019
81e99d8
more details on SimpleExt11 configs
echarles Nov 16, 2019
edc21fc
simple_app11: hello flag set True to hell config
echarles Nov 16, 2019
ae3d27c
add the correct json in setup.py
echarles Nov 22, 2019
651e91c
Merge branch 'master' into example
echarles Dec 3, 2019
f65c74e
more work on settings
echarles Dec 3, 2019
25504c8
add configuration files
echarles Dec 3, 2019
1b58d29
comment logs
echarles Dec 4, 2019
ff341a1
update readme
echarles Dec 4, 2019
cb046b5
Merge branch 'master' into example
echarles Dec 13, 2019
505fafc
Merge branch 'master' into example
echarles Dec 14, 2019
f15e523
Merge branch 'master' into example
echarles Jan 9, 2020
43b7447
rename configs
echarles Jan 9, 2020
295d7c4
typo
echarles Jan 9, 2020
269fc01
add = when passing the SimpleApp1.configA via CLI
echarles Jan 14, 2020
4a64388
use JinjaMixin
echarles Jan 16, 2020
8aca33c
Merge branch 'master' into example
echarles Feb 29, 2020
c3a1940
align examples to mixin
echarles Feb 29, 2020
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ config.rst

package-lock.json
geckodriver.log
yarn.lock
82 changes: 82 additions & 0 deletions examples/simple/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Jupyter Server Simple Extension Example

This folder contains an example of 2 simple extensions on top of Jupyter Server.

## Install

You need `python3` to build and run the server extensions.

```bash
conda create -y -n jext python=3.7
conda activate jext
pip install -e .
```

**OPTIONAL** If you want to build the Typescript code, you need `npm` on your local env. Compiled javascript is provided as artifact in this repository, so this Typescript build step is optional. The Typescript source and configuration has been taken from https://github.com/markellekelly/jupyter-server-example.

```bash
npm install
npm run build
```
Zsailer marked this conversation as resolved.
Show resolved Hide resolved

## Start Extension 1 and Extension 2

```bash
# Start the jupyter server, it will load both simple_ext1 and simple_ext2 based on the provided trait.
jupyter server --ServerApp.jpserver_extensions="{'simple_ext1': True, 'simple_ext2': True}"
```

Optionally, you can copy `simple_ext1.json` and `simple_ext2.json` configuration to your env `etc` folder and start only Extension 1, which will also start Extension 2.

```bash
pip uninstall -y simple_ext
python setup.py install
cp -r ./etc $(dirname $(which jupyter))/..
# Start the jupyter server extension simple_ext1, it will also load simple_ext2 because of load_other_extensions = True..
jupyter simple-ext1
```

Now you can render Server content in your browser.

```bash
# Default server page should redirect to `/static/simple_ext1/favicon.ico`.
open http://localhost:8888
```

Render Extension 1 Server content in your browser.

```bash
# Favicon static content.
open http://localhost:8888/static/simple_ext1/favicon.ico
# HTML static page.
open http://localhost:8888/static/simple_ext1/test.html
# Content from Handlers.
open http://localhost:8888/simple_ext1/params/test?var1=foo
# Content from Template.
open http://localhost:8888/simple_ext1/page1/test
# Content from Template with Typescript.
open http://localhost:8888/simple_ext1
open http://localhost:8888/simple_ext1/template
# Error content.
open http://localhost:8888/simple_ext1/nope
```

Render Extension 2 Server content in your browser.

```bash
# HTML static page.
open http://localhost:8888/static/simple_ext2/test.html
# Content from Handlers.
open http://localhost:8888/simple_ext2/params/test?var1=foo
```

## Start only Extension 2

Now stop the server and start again with only Extension 2.

```bash
# Start the jupyter server extension simple_ext2, it will NOT load simple_ext1 because of load_other_extensions = False.
jupyter simple-ext2
```

Try with the above links to check that only Extension 2 is responding (Extension 1 URLs should give you an error).
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ServerApp": {
"jpserver_extensions": {
"simple_ext1": true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ServerApp": {
"jpserver_extensions": {
"simple_ext2": true
}
}
}
18 changes: 18 additions & 0 deletions examples/simple/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "jupyter-simple-ext",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "tsc -p src && webpack",
"clean": "rimraf build",
"prepublishOnly": "npm run build"
},
"dependencies": {},
"devDependencies": {
"rifraf": "2.0.3",
"webpack": "~4.29.6",
"webpack-cli": "^3.3.0",
"whatwg-fetch": "~2.0.3",
"typescript": "3.6.4"
}
}
46 changes: 46 additions & 0 deletions examples/simple/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os, setuptools
from setuptools import find_packages

VERSION = '0.0.1'

def get_data_files():
"""Get the data files for the package.
"""
data_files = [
('etc/jupyter/jupyter_server_config.d', ['etc/jupyter/jupyter_server_config.d/simple_ext.json']),
]
def add_data_files(path):
for (dirpath, dirnames, filenames) in os.walk(path):
if filenames:
data_files.append((dirpath, [os.path.join(dirpath, filename) for filename in filenames]))
# Add all the templates
add_data_files('simple_ext1/static')
add_data_files('simple_ext1/templates')
add_data_files('simple_ext2/static')
add_data_files('simple_ext2/templates')
return data_files

setuptools.setup(
name = 'simple_ext',
version = VERSION,
description = 'Jupyter Simple Extension',
long_description = open('README.md').read(),
packages = find_packages(),
python_requires = '>=3.5',
install_requires = [
'jupyter_server==0.1.1',
echarles marked this conversation as resolved.
Show resolved Hide resolved
'jinja2',
],
tests_requires = [
'pytest',
'pytest-cov',
'pylint',
],
data_files = get_data_files(),
entry_points = {
'console_scripts': [
'jupyter-simple-ext1 = simple_ext1.application:main',
'jupyter-simple-ext2 = simple_ext2.application:main'
]
},
)
8 changes: 8 additions & 0 deletions examples/simple/simple_ext1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .application import SimpleApp1

def _jupyter_server_extension_paths():
return [
{'module': 'simple_ext1'}
]

load_jupyter_server_extension = SimpleApp1.load_jupyter_server_extension
56 changes: 56 additions & 0 deletions examples/simple/simple_ext1/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os, jinja2
from jupyter_server.extension.application import ExtensionApp
from .handlers import RedirectHandler, ParameterHandler, TemplateHandler, Page1Handler, IndexHandler, ErrorHandler

DEFAULT_STATIC_FILES_PATH = os.path.join(os.path.dirname(__file__), "static")
DEFAULT_TEMPLATE_FILES_PATH = os.path.join(os.path.dirname(__file__), "templates")

class SimpleApp1(ExtensionApp):

# The name of the extension
extension_name = "simple_ext1"

# Te url that your extension will serve its homepage.
default_url = '/simple_ext1'

# Should your extension expose other server extensions when launched directly?
load_other_extensions = True

# Local path to static files directory.
static_paths = [
DEFAULT_STATIC_FILES_PATH
]

# Local path to templates directory.
template_paths = [
DEFAULT_TEMPLATE_FILES_PATH
]

def initialize_handlers(self):
self.handlers.extend([
(r'/', RedirectHandler),
(r'/simple_ext1/params/(.+)$', ParameterHandler),
(r'/simple_ext1/template', TemplateHandler),
(r'/simple_ext1/page1/(.*)$', Page1Handler),
(r'/simple_ext1/?', IndexHandler),
(r'/simple_ext1/(.*)', ErrorHandler)
])

def initialize_templates(self):
jenv_opt = {"autoescape": True}
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(self.template_paths),
extensions=["jinja2.ext.i18n"],
**jenv_opt
)
template_settings = {"simple_ext1_jinja2_env": env}
self.settings.update(**template_settings)

def initialize_settings(self):
pass

#-----------------------------------------------------------------------------
# Main entry point
#-----------------------------------------------------------------------------

main = launch_new_instance = SimpleApp1.launch_instance
42 changes: 42 additions & 0 deletions examples/simple/simple_ext1/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from jupyter_server.extension.handler import ExtensionHandler

class TemplateHandler(ExtensionHandler):

def get_template(self, name):
"""Return the jinja template object for a given name"""
return self.settings['simple_ext1_jinja2_env'].get_template(name)

class RedirectHandler(ExtensionHandler):
def get(self):
self.redirect("/static/simple_ext1/favicon.ico")

class IndexHandler(TemplateHandler):

def get(self):
self.write(self.render_template("index.html"))

class ParameterHandler(ExtensionHandler):

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]
self.write('<h1>Hello Simple App 1 from Handler.</h1>')
self.write('<p>matched_part: {}</p>'.format(matched_part))
self.write('<p>var1: {}</p>'.format(var1))
self.write('<p>components: {}</p>'.format(components))

class TemplateHandler(TemplateHandler):

def get(self):
print(self.get_template('simple_ext1.html'))
self.write(self.render_template('simple_ext1.html'))

class Page1Handler(TemplateHandler):

def get(self, path):
self.write(self.render_template('page1.html', text=path))

class ErrorHandler(TemplateHandler):

def get(self, path):
self.write(self.render_template('error.html'))
111 changes: 111 additions & 0 deletions examples/simple/simple_ext1/static/bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({

/***/ "./simple_ext1/static/index.js":
/*!*************************************!*\
!*** ./simple_ext1/static/index.js ***!
\*************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("function main() {\n let div = document.getElementById(\"mydiv\");\n div.innerText = \"Hello from Typescript\";\n}\nwindow.addEventListener('load', main);\n\n\n//# sourceURL=webpack:///./simple_ext1/static/index.js?");

/***/ }),

/***/ 0:
/*!*******************************************!*\
!*** multi ./simple_ext1/static/index.js ***!
\*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

eval("module.exports = __webpack_require__(/*! ./simple_ext1/static/index.js */\"./simple_ext1/static/index.js\");\n\n\n//# sourceURL=webpack:///multi_./simple_ext1/static/index.js?");

/***/ })

/******/ });
Binary file added examples/simple/simple_ext1/static/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions examples/simple/simple_ext1/static/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare function main(): void;
5 changes: 5 additions & 0 deletions examples/simple/simple_ext1/static/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function main() {
let div = document.getElementById("mydiv");
div.innerText = "Hello from Typescript";
}
window.addEventListener('load', main);
Loading