jupyterlab / extension-cookiecutter-ts

A cookiecutter recipe for JupyterLab extensions in Typescript
BSD 3-Clause "New" or "Revised" License
179 stars 88 forks source link

Binder postBuild missing steps? #153

Closed chrisjsewell closed 3 years ago

chrisjsewell commented 3 years ago

Description

It feels like the Binder postBuild is missing a few steps. For an extension with server, this works to display my extension:

...

# verify the environment the extension didn't break anything
_(sys.executable, "-m", "pip", "check")

# Install the node dependencies
_("jlpm")

# build the labextension
_("jlpm", "run", "build")

# link the labextension to JupyterLab
_("jupyter", "labextension", "develop", ".", "--overwrite")

# install server
_("jupyter", "server", "extension", "enable", "jlab_aiidatree")

However, there is still a problem for me that POST requests to the server are failing (see below for full output), even though this works fine locally.

Perhaps you know a fix?

Reproduce

See binder link of https://github.com/chrisjsewell/jlab_aiidatree

Expected behavior

The extension is loaded on Binder load

Context

Post request failure:

Not a JSON response body. 
Response {type: "basic", url: "https://hub-binder.mybinder.ovh/user/chrisjsewell-…x5b44ro/jlab_aiidatree/querybuilder?1620960326400", redirected: false, status: 404, ok: false, …}
body: ReadableStream
bodyUsed: true
headers: Headers
__proto__: Headers
ok: false
redirected: false
status: 404
statusText: ""
type: "basic"
url: "https://hub-binder.mybinder.ovh/user/chrisjsewell-jlab_aiidatree-zx5b44ro/jlab_aiidatree/querybuilder?1620960326400"
Error on POST /jlab_aiidatree/querybuilder [object Object].
Error: <!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">

    <title>Jupyter Notebook</title>
    <link id="favicon" rel="shortcut icon" type="image/x-icon" href="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/base/images/favicon.ico?v=50afa725b5de8b00030139d09b38620224d4e7dba47c07ef0e86d4643f30c9bfe6bb7e1a4a1c561aa32834480909a4b6fe7cd1e17f7159330b6b5914bf45a880">
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="stylesheet" href="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/jquery-ui/themes/smoothness/jquery-ui.min.css?v=fb45616eef2c454960f91fcd2a04efeda84cfacccf0c5d741ba2793dc1dbd6d3ab01aaae6485222945774c7d7a9a2e9fb87e0d8ef1ea96893aa6906147a371bb" type="text/css" />
    <link rel="stylesheet" href="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/jquery-typeahead/dist/jquery.typeahead.min.css?v=5edf53bf6bb9c3b1ddafd8594825a7e2ed621f19423e569c985162742f63911c09eba2c529f8fb47aebf27fafdfe287d563347f58c1126b278189a18871b6a9a" type="text/css" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/style/style.min.css?v=56dfd556850eb17b7998c6828467598a322b41593edc758739c66cb2c3fea98f23d0dd8bf8b9b0f5d67bb976a50e4c34f789fe640cbb440fa089e1bf5ec170bd" type="text/css"/>

<style type="text/css">
/* disable initial hide */
div#header, div#site {
    display: block;
}
</style>

    <link rel="stylesheet" href="/user/chrisjsewell-jlab_aiidatree-9zlte1op/custom/custom.css" type="text/css" />
    <script src="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/es6-promise/promise.min.js?v=bea335d74136a63ae1b5130f5ac9a50c6256a5f435e6e09fef599491a84d834a8b0f011ca3eaaca3b4ab6a2da2d3e1191567a2f171e60da1d10e5b9d52f84184" type="text/javascript" charset="utf-8"></script>
    <script src="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/react/react.production.min.js?v=9a0aaf84a316c8bedd6c2ff7d5b5e0a13f8f84ec02442346cba0b842c6c81a6bf6176e64f3675c2ebf357cb5bb048e0b527bd39377c95681d22468da3d5de735" type="text/javascript"></script>
    <script src="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/react/react-dom.production.min.js?v=6fc58c1c4736868ff84f57bd8b85f2bdb985993a9392718f3b4af4bfa10fb4efba2b4ddd68644bd2a8daf0619a3844944c9c43f8528364a1aa6fc01ec1b8ae84" type="text/javascript"></script>
    <script src="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/create-react-class/index.js?v=894ad57246e682b4cfbe7cd5e408dcd6b38d06af4de4f3425991e2676fdc2ef1732cbd19903104198878ae77de12a1996de3e7da3a467fb226bdda8f4618faec" type="text/javascript"></script>
    <script src="/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/components/requirejs/require.js?v=d37b48bb2137faa0ab98157e240c084dd5b1b5e74911723aa1d1f04c928c2a03dedf922d049e4815f7e5a369faa2e6b6a1000aae958b7953b5cc60411154f593" type="text/javascript" charset="utf-8"></script>
    <script>
      require.config({

          urlArgs: "v=20210514014226",

          baseUrl: '/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/',
          paths: {
            'auth/js/main': 'auth/js/main.min',
            custom : '/user/chrisjsewell-jlab_aiidatree-9zlte1op/custom',
            nbextensions : '/user/chrisjsewell-jlab_aiidatree-9zlte1op/nbextensions',
            kernelspecs : '/user/chrisjsewell-jlab_aiidatree-9zlte1op/kernelspecs',
            underscore : 'components/underscore/underscore-min',
            backbone : 'components/backbone/backbone-min',
            jed: 'components/jed/jed',
            jquery: 'components/jquery/jquery.min',
            json: 'components/requirejs-plugins/src/json',
            text: 'components/requirejs-text/text',
            bootstrap: 'components/bootstrap/dist/js/bootstrap.min',
            bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
            'jquery-ui': 'components/jquery-ui/jquery-ui.min',
            moment: 'components/moment/min/moment-with-locales',
            codemirror: 'components/codemirror',
            termjs: 'components/xterm.js/xterm',
            typeahead: 'components/jquery-typeahead/dist/jquery.typeahead.min',
          },
          map: { // for backward compatibility
              "*": {
                  "jqueryui": "jquery-ui",
              }
          },
          shim: {
            typeahead: {
              deps: ["jquery"],
              exports: "typeahead"
            },
            underscore: {
              exports: '_'
            },
            backbone: {
              deps: ["underscore", "jquery"],
              exports: "Backbone"
            },
            bootstrap: {
              deps: ["jquery"],
              exports: "bootstrap"
            },
            bootstraptour: {
              deps: ["bootstrap"],
              exports: "Tour"
            },
            "jquery-ui": {
              deps: ["jquery"],
              exports: "$"
            }
          },
          waitSeconds: 30,
      });

      require.config({
          map: {
              '*':{
                'contents': 'services/contents',
              }
          }
      });

      // error-catching custom.js shim.
      define("custom", function (require, exports, module) {
          try {
              var custom = require('custom/custom');
              console.debug('loaded custom.js');
              return custom;
          } catch (e) {
              console.error("error loading custom.js", e);
              return {};
          }
      })

    document.nbjs_translations = {"domain": "nbjs", "locale_data": {"nbjs": {"": {"domain": "nbjs"}}}};
    document.documentElement.lang = navigator.language.toLowerCase();
    </script>

</head>

<body class=""

    data-jupyter-api-token="EC36h18LRE6j6_ZE-_IMVg"

dir="ltr">

<noscript>
    <div id='noscript'>
      Jupyter Notebook requires JavaScript.<br>
      Please enable it to proceed. 
  </div>
</noscript>

<div id="header" role="navigation" aria-label="Top Menu">
  <div id="header-container" class="container">
  <div id="ipython_notebook" class="nav navbar-brand"><a href="/user/chrisjsewell-jlab_aiidatree-9zlte1op/tree?token=EC36h18LRE6j6_ZE-_IMVg" title='dashboard'>
      <img src='/user/chrisjsewell-jlab_aiidatree-9zlte1op/static/base/images/logo.png?v=a2a176ee3cee251ffddf5fa21fe8e43727a9e5f87a06f9c91ad7b776d9e9d3d5e0159c16cc188a3965e00375fb4bc336c16067c688f5040c0c2d4bfdb852a9e4' alt='Jupyter Notebook'/>
  </a></div>

  <span>
    <a id="visit-repo-link" href="https://github.com/chrisjsewell/jlab_aiidatree/tree/ad90a5a40e2d4c480095f59c1967fb4e6bb8502a" class="btn btn-default btn-sm navbar-btn" target="_blank"
       style="margin-right: 2px; margin-left: 4px;">Visit repo</a>
  </span>

  <span>
    <button id="copy-binder-link" title="Copy binder link to clipboard" class="btn btn-default btn-sm navbar-btn"
            style="margin-right: 0; margin-left: 2px;"
            data-url="https://mybinder.org/v2/gh/chrisjsewell/jlab_aiidatree/ad90a5a40e2d4c480095f59c1967fb4e6bb8502a" onclick="copy_link_into_clipboard(this);">
      Copy Binder link
    </button>
  </span>

  </div>
  <div class="header-bar"></div>

</div>

<div id="site">

<div class="error">

    <h1>404 : Not Found</h1>

<p>You are requesting a page that does not exist!</p>

</div>

</div>

<script type='text/javascript'>
    function copy_link_into_clipboard(b) {
        var $temp = $("<input>");
        $(b).parent().append($temp);
        $temp.val($(b).data('url')).select();
        document.execCommand("copy");
        $temp.remove();
    }
</script>

<script type='text/javascript'>
require(['jquery'], function($) {
  // scroll long tracebacks to the bottom
  var tb = $(".traceback")[0];
  tb.scrollTop = tb.scrollHeight;
});
</script>

<script type='text/javascript'>
  function _remove_token_from_url() {
    if (window.location.search.length <= 1) {
      return;
    }
    var search_parameters = window.location.search.slice(1).split('&');
    for (var i = 0; i < search_parameters.length; i++) {
      if (search_parameters[i].split('=')[0] === 'token') {
        // remote token from search parameters
        search_parameters.splice(i, 1);
        var new_search = '';
        if (search_parameters.length) {
          new_search = '?' + search_parameters.join('&');
        }
        var new_url = window.location.origin + 
                      window.location.pathname + 
                      new_search + 
                      window.location.hash;
        window.history.replaceState({}, "", new_url);
        return;
      }
    }
  }
  _remove_token_from_url();
</script>
</body>

</html>
welcome[bot] commented 3 years ago

Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! :hugs:
If you haven't done so already, check out Jupyter's Code of Conduct. Also, please try to follow the issue template as it helps other other community members to contribute more effectively. welcome You can meet the other Jovyans by joining our Discourse forum. There is also an intro thread there where you can stop by and say Hi! :wave:
Welcome to the Jupyter community! :tada:

chrisjsewell commented 3 years ago

thanks otherwise, the extension tutorial + this cookiecutter made it pretty easy to get my extension up and running!

jtpio commented 3 years ago

Thanks @chrisjsewell for the report.

Would you be able to check whether removing the -e part of the following command does the trick?

https://github.com/jupyterlab/extension-cookiecutter-ts/blob/12c14521137244f8297b82ecc1b6d55327b487be/%7B%7Bcookiecutter.python_name%7D%7D/binder/postBuild#L33

Looks like this might be related to the recent update of jupyter-packaging.

However, there is still a problem for me that POST requests to the server are failing (see below for full output), even though this works fine locally.

Maybe the server extension is not correctly activated. This could be checked on Binder by opening a terminal and running:

jupyter serverextension list   # binder starts a classic notebook server by default for now
jupyter server extension list  # just to double check
s-weigand commented 3 years ago

Would you be able to check whether removing the -e part of the following command does the trick?

This fixed it for me with a client-side only extension that didn't console.log that it is installed with the -e flag in place.

fcollonval commented 3 years ago

The root cause is that BinderHub is not using the new jupyter server logic. So a server extension needs to be made backward compatible to work: see https://github.com/jupyterlab/jupyterlab-git/pull/863