diff --git a/dev-requirements.txt b/dev-requirements.txt index bdf678f1..0a8b925e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,6 @@ jupyter-packaging>=0.10 nbclassic +notebook>=5.5,<7 packaging pytest pytest-cov diff --git a/nbgitpuller/__init__.py b/nbgitpuller/__init__.py index 18da0ccb..47973f96 100644 --- a/nbgitpuller/__init__.py +++ b/nbgitpuller/__init__.py @@ -1,7 +1,6 @@ from .version import __version__ # noqa -from .handlers import SyncHandler, UIHandler, LegacyInteractRedirectHandler, LegacyGitSyncRedirectHandler from .pull import GitPuller # noqa -from notebook.utils import url_path_join +from jupyter_server.utils import url_path_join from tornado.web import StaticFileHandler import os @@ -33,6 +32,19 @@ def _load_jupyter_server_extension(app): - notebook: https://jupyter-notebook.readthedocs.io/en/stable/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.html#Example---Server-extension - jupyter_server: https://jupyter-server.readthedocs.io/en/latest/developers/extensions.html """ + # identify base handler by app class + # must do this before importing from .handlers + from ._compat import get_base_handler + + get_base_handler(app) + + from .handlers import ( + SyncHandler, + UIHandler, + LegacyInteractRedirectHandler, + LegacyGitSyncRedirectHandler, + ) + web_app = app.web_app base_url = url_path_join(web_app.settings['base_url'], 'git-pull') handlers = [ diff --git a/nbgitpuller/_compat.py b/nbgitpuller/_compat.py new file mode 100644 index 00000000..036967c9 --- /dev/null +++ b/nbgitpuller/_compat.py @@ -0,0 +1,40 @@ +"""Import base Handler classes from Jupyter Server or Notebook + +Must be called before importing .handlers to ensure the correct base classes +""" +import warnings + +_JupyterHandler = None + + +def get_base_handler(app=None): + """Get the base JupyterHandler class to use + + Inferred from app class (either jupyter_server or notebook app) + """ + global _JupyterHandler + if _JupyterHandler is not None: + return _JupyterHandler + if app is None: + warnings.warn( + "Guessing base JupyterHandler class. Specify an app to ensure the right JupyterHandler is used.", + stacklevel=2, + ) + from jupyter_server.base.handlers import JupyterHandler + return JupyterHandler + + top_modules = {cls.__module__.split(".", 1)[0] for cls in app.__class__.mro()} + if "jupyter_server" in top_modules: + from jupyter_server.base.handlers import JupyterHandler + + _JupyterHandler = JupyterHandler + return _JupyterHandler + if "notebook" in top_modules: + from notebook.base.handlers import IPythonHandler + + _JupyterHandler = IPythonHandler + return _JupyterHandler + + warnings.warn(f"Failed to detect base JupyterHandler class for {app}.", stacklevel=2) + from jupyter_server.base.handlers import JupyterHandler + return JupyterHandler diff --git a/nbgitpuller/handlers.py b/nbgitpuller/handlers.py index a88f3251..2e572768 100644 --- a/nbgitpuller/handlers.py +++ b/nbgitpuller/handlers.py @@ -2,7 +2,6 @@ import traceback import urllib.parse -from notebook.base.handlers import IPythonHandler import threading import json import os @@ -11,6 +10,9 @@ from .pull import GitPuller from .version import __version__ +from ._compat import get_base_handler + +JupyterHandler = get_base_handler() jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader( @@ -18,7 +20,7 @@ ), ) -class SyncHandler(IPythonHandler): +class SyncHandler(JupyterHandler): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -132,7 +134,7 @@ def pull(): self.git_lock.release() -class UIHandler(IPythonHandler): +class UIHandler(JupyterHandler): @web.authenticated async def get(self): app_env = os.getenv('NBGITPULLER_APP', default='notebook') @@ -169,7 +171,7 @@ async def get(self): await self.flush() -class LegacyGitSyncRedirectHandler(IPythonHandler): +class LegacyGitSyncRedirectHandler(JupyterHandler): """ The /git-pull endpoint was previously exposed /git-sync. @@ -185,7 +187,7 @@ async def get(self): self.redirect(new_url) -class LegacyInteractRedirectHandler(IPythonHandler): +class LegacyInteractRedirectHandler(JupyterHandler): """ The /git-pull endpoint was previously exposed /interact. diff --git a/setup.py b/setup.py index 3efef420..32fb0347 100644 --- a/setup.py +++ b/setup.py @@ -32,13 +32,13 @@ author='Peter Veerman, YuviPanda', author_email='peterkangveerman@gmail.com', cmdclass=cmdclass, - description='Notebook Extension to do one-way synchronization of git repositories', + description='Jupyter Extension to do one-way synchronization of git repositories', long_description=open('README.md').read(), long_description_content_type='text/markdown', packages=find_packages(), include_package_data=True, platforms='any', - install_requires=['notebook>=5.5.0', 'jupyter_server>=1.10.1', 'tornado'], + install_requires=['jupyter_server>=1.10.1', 'tornado'], data_files=[ ('etc/jupyter/jupyter_server_config.d', ['nbgitpuller/etc/jupyter_server_config.d/nbgitpuller.json']), ('etc/jupyter/jupyter_notebook_config.d', ['nbgitpuller/etc/jupyter_notebook_config.d/nbgitpuller.json']) diff --git a/tests/test_api.py b/tests/test_api.py index 55172ca9..d5cb1c31 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -118,6 +118,23 @@ def test_clone_default(jupyterdir, jupyter_server): assert os.path.isdir(os.path.join(target_path, '.git')) +def test_clone_auth(jupyterdir, jupyter_server): + """ + Tests use of 'repo' and 'branch' parameters. + """ + with Remote() as remote, Pusher(remote) as pusher: + pusher.push_file('README.md', 'Testing some content') + print(f'path: {remote.path}') + params = { + 'repo': remote.path, + 'branch': 'master', + 'token': 'wrong', + } + r = request_api(params) + # no token, redirect to login + assert r.code == 302 + + def test_clone_targetpath(jupyterdir, jupyter_server): """ Tests use of 'targetpath' parameter.