Skip to content
Open
Show file tree
Hide file tree
Changes from 15 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
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# This is a GitHub workflow defining a set of jobs with a set of steps.
# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions
#
name: Test

on:
pull_request:
paths-ignore:
- "**.md"
- "**.rst"
- ".github/workflows/*"
- "!.github/workflows/test.yaml"
push:
paths-ignore:
- "**.md"
- "**.rst"
- ".github/workflows/*"
- "!.github/workflows/test.yaml"
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.13"]

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "${{ matrix.python-version }}"

- uses: actions/setup-node@v4
with:
cache: yarn
node-version: 20.x
registry-url: https://registry.npmjs.org
cache-dependency-path: yarn.lock

- name: Update root build packages
run: |
pip install --upgrade build pip
- name: Build Python package
run: |
pyproject-build
- name: Install Python package
run: |
pip install -vv '.[test]' 'jupyterlab>4.0'
- name: List Python packages
run: |
pip freeze
pip check
- name: Run tests
run: pytest -vvvv tests

- name: Check the Jupyter Server extension is installed
run: |
jupyter server extension list
jupyter server extension list 2>&1 | grep -iE "jupyterlmod.*OK" -
- name: Check the lab extension
run: |
jupyter labextension list
jupyter labextension list 2>&1 | grep -iE '@cmd-ntrf/jupyterlab-lmod.*OK.*'
python -m jupyterlab.browser_check
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ lib/
node_modules/
*.egg-info/
.ipynb_checkpoints
.coverage
.yarn/
21 changes: 7 additions & 14 deletions jupyterlmod/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import os
import sys

import json
from jupyter_server.utils import url_path_join as ujoin
from pathlib import Path

from .config import Module as ModuleConfig
from .handler import default_handlers, PinsHandler, ModuleSystemLogoHandler
from .handler import default_handlers, logo_handler, PinsHandler

from module import MODULE_SYSTEM

HERE = Path(__file__).parent.resolve()

Expand All @@ -29,7 +27,8 @@ def _jupyter_server_extension_points():
def _jupyter_nbextension_paths():
return [
dict(
section="tree", src="static", dest="jupyterlmod", require="jupyterlmod/main"
section="tree", src="static", dest="jupyterlmod",
require="jupyterlmod/main"
)
]

Expand All @@ -54,16 +53,10 @@ def _load_jupyter_server_extension(nbapp):
for path, class_ in default_handlers:
web_app.add_handlers(".*$", [(ujoin(base_url, path), class_)])

web_app.add_handlers(".*$", logo_handler)
web_app.add_handlers(".*$", [
(ujoin(base_url, 'module/launcher-pins'), PinsHandler, {'launcher_pins': launcher_pins}),
])

logo_path = os.path.join(
sys.prefix, 'share', 'jupyter', 'nbextensions', 'jupyterlmod', 'logos',
f'{MODULE_SYSTEM}.png'
)
web_app.add_handlers(".*$", [
(ujoin(base_url, 'module/logo'), ModuleSystemLogoHandler, {'path': logo_path}),
(ujoin(base_url, 'module/launcher-pins'), PinsHandler,
{'launcher_pins': launcher_pins}),
])

# For backward compatibility
Expand Down
15 changes: 12 additions & 3 deletions jupyterlmod/handler.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""List of handlers to register"""

import json
import os

import module
import sys
import json

from functools import wraps
from glob import glob
Expand All @@ -13,6 +12,8 @@

from jupyter_server.base.handlers import JupyterHandler

import module


def jupyter_path_decorator(func):
@wraps(func)
Expand All @@ -24,6 +25,10 @@ async def wrapper(self, *args, **kwargs):
return wrapper


LOGO_PATH = os.path.join(
sys.prefix, 'share', 'jupyter', 'nbextensions', 'jupyterlmod', 'logos',
f'{module.MODULE_SYSTEM}.png'
)
MODULE = module.ModuleAPI()


Expand Down Expand Up @@ -185,3 +190,7 @@ async def get(self):
(r"/module/paths", ModulePaths),
(r"/module/folders/(.*)", FoldersHandler)
]

logo_handler = [
(r"/module/logo", ModuleSystemLogoHandler, {'path': LOGO_PATH}),
]
22 changes: 14 additions & 8 deletions module/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def module(command, *args):
"""
# If MODULE_CMD is empty return
if not MODULE_CMD:
return 'Module system not found'
return 'No module system found'

cmd = MODULE_CMD, "python", "--terse", command, *args

Expand Down Expand Up @@ -116,7 +116,7 @@ def print_output_decorator(function):
"""
Returns a wrapper that captures output, if produced and prints it

:param function: Callable function
:param function: Async callable function
:type paths: callable
:return: Wrapper function
:rtype: callable
Expand Down Expand Up @@ -198,11 +198,15 @@ async def list(self, include_hidden=False):
self.list_cache = {True: [], False: []}
string = await module("list")
if string and not string.startswith(EMPTY_LIST_STR):
modules = string.split()
self.list_cache[True] = modules
loaded_modules = MODULE_REGEX.findall(string.strip())
loaded_hidden_modules = [
name for name in loaded_modules
if MODULE_HIDDEN_REGEX.match(name)
]
self.list_cache[True] = loaded_modules
self.list_cache[False] = [
name for name in modules
if not MODULE_HIDDEN_REGEX.match(name)
name for name in loaded_modules
if name not in loaded_hidden_modules
]
return self.list_cache[include_hidden]

Expand Down Expand Up @@ -248,7 +252,7 @@ async def reset(self):
:return: Stderr if command failed
:rtype: str
"""
if await self.module_system() == 'tmod':
if await self.system() == 'tmod':
return 'subcommand reset does not exist in environment modules (tmod)'
output = await module("reset")
self.invalidate_module_caches()
Expand Down Expand Up @@ -294,7 +298,9 @@ async def savelist(self):
if self.savelist_cache is None:
string = await module("savelist")
if string is not None:
self.savelist_cache = string.split()
self.savelist_cache = [
m for m in string.splitlines() if 'Named collection list:' not in m
]
else:
self.savelist_cache = []
return self.savelist_cache
Expand Down
52 changes: 48 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,32 @@ dynamic = [
]
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.6"
requires-python = ">=3.9"
classifiers = [
"Framework :: Jupyter",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"jupyter-core",
"jupyter-server",
]

[project.optional-dependencies]
test = [
"pytest",
"pytest-cov",
"pytest-mock",
"pytest-html",
"pytest-jupyter[server]>=0.6",
]

[tool.hatch.metadata.hooks.nodejs]
path = "package.json"
fields = ["description", "authors", "urls"]
Expand Down Expand Up @@ -70,10 +79,14 @@ exclude = [
ensured-targets = [
"jupyterlmod/labextension/package.json",
]
skip-if-exists = [
"jupyterlmod/labextension/package.json",
]
dependencies = [
"hatch-jupyter-builder>=0.8.2",
]
build-function = "hatch_jupyter_builder.npm_builder"
optional-editable-build = true

[tool.hatch.build.hooks.jupyter-builder.build-kwargs]
path = "."
Expand All @@ -83,6 +96,13 @@ npm = [
"jlpm",
]

[tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs]
path = "."
build_cmd = "build"
npm = ["jlpm"]
source_dir = "src"
build_dir = "jupyterlmod/labextension"

[tool.tbump]
field = [
{ name = "channel", default = "" },
Expand All @@ -100,3 +120,27 @@ tag_template = "v{new_version}"
[[tool.tbump.file]]
src = "pyproject.toml"
version_template = "version = \"{major}.{minor}.{patch}{channel}{release}\""

[tool.pytest.ini_options]
cache_dir = "build/.cache/pytest"
addopts = [
"-vv",
"--cov=jupyterlmod",
"--cov=module",
"--cov-branch",
"--cov-context=test",
"--cov-report=term-missing:skip-covered",
"--cov-report=html:build/coverage",
"--html=build/pytest/index.html",
"--color=yes",
]

[tool.coverage.run]
data_file = "build/.coverage"
concurrency = [
"multiprocessing",
"thread"
]

[tool.coverage.html]
show_contexts = true
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading