diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 3cdea3eec0..8492e036b1 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -37,34 +37,20 @@ jobs: sudo apt-get update sudo apt-get install texlive-plain-generic inkscape texlive-xetex sudo apt-get install xvfb x11-utils libxkbcommon-x11-0 pandoc - - name: Run the tests + - name: Run the tests on posix if: ${{ !startsWith(matrix.python-version, 'pypy') && !startsWith(matrix.os, 'windows') }} - run: hatch run cov:test -W default || hatch run test:test -W default --lf - - name: Run the tests on pypy and windows - if: ${{ startsWith(matrix.python-version, 'pypy') || startsWith(matrix.os, 'windows') }} - run: hatch run test:test -W default || hatch run test:test -W default --lf + run: hatch run cov:test --cov-fail-under 75 || hatch run test:test --lf + - name: Run the tests on pypy + if: ${{ startsWith(matrix.python-version, 'pypy') }} + run: hatch run test:test || hatch run test:test --lf + - name: Run the tests on windows + if: ${{ startsWith(matrix.python-version, 'windows') }} + run: hatch run cov:nowarn -s || hatch run cov:nowarn --lf - name: Coverage run: | pip install codecov codecov - client8: - runs-on: ${{ matrix.os }} - timeout-minutes: 20 - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.10"] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Base Setup - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - run: | - pip install -U pre jupyter_client - hatch run test:test || hatch run test:test --lf - pre-commit: name: pre-commit runs-on: ubuntu-latest @@ -102,38 +88,30 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Base Setup - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 + - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 with: python_version: "3.8" - - name: Install miniumum versions - uses: jupyterlab/maintainer-tools/.github/actions/install-minimums@v1 + - uses: jupyterlab/maintainer-tools/.github/actions/install-minimums@v1 + with: + only_create_file: 1 - name: Run the unit tests run: | - pytest -vv -W default || pytest -vv -W default --lf + export PIP_CONSTRAINT="./contraints_file.txt" + hatch run test:nowarn || hatch run test:nowarn --lf test_prereleases: name: Test Prereleases runs-on: ubuntu-latest timeout-minutes: 20 steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Base Setup - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 + - uses: actions/checkout@v3 + - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 with: python_version: "3.11" - - name: Install the Python dependencies - run: | - pip install --no-deps . - pip install --pre --upgrade ".[test]" - - name: List installed packages - run: | - pip freeze - pip check - name: Run the tests run: | - pytest -vv -W default || pytest -vv -W default --lf + export PIP_PRE=1 + hatch run test:nowarn || hatch run test:nowarn --lf make_sdist: name: Make SDist @@ -173,7 +151,6 @@ jobs: - test_docs - test_minimum_versions - test_prereleases - - client8 - check_links - test_sdist runs-on: ubuntu-latest diff --git a/jupyter_server/files/handlers.py b/jupyter_server/files/handlers.py index de60117324..1c065ab38a 100644 --- a/jupyter_server/files/handlers.py +++ b/jupyter_server/files/handlers.py @@ -1,7 +1,6 @@ """Serve files directly from the ContentsManager.""" # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -import json import mimetypes from base64 import decodebytes from typing import List @@ -15,7 +14,7 @@ AUTH_RESOURCE = "contents" -class FilesHandler(JupyterHandler): +class FilesHandler(JupyterHandler, web.StaticFileHandler): """serve files via ContentsManager Normally used when ContentsManager is not a FileContentsManager. @@ -85,8 +84,6 @@ async def get(self, path, include_body=True): if model["format"] == "base64": b64_bytes = model["content"].encode("ascii") self.write(decodebytes(b64_bytes)) - elif model["format"] == "json": - self.write(json.dumps(model["content"])) else: self.write(model["content"]) self.flush() diff --git a/pyproject.toml b/pyproject.toml index 5e286213aa..3b0e37242a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,7 +104,8 @@ dependencies = ["coverage", "pytest-cov"] [tool.hatch.envs.cov.env-vars] ARGS = "-vv --cov jupyter_server --cov-branch --cov-report term-missing:skip-covered" [tool.hatch.envs.cov.scripts] -test = "python -m pytest $ARGS --cov-fail-under 75 {args}" +test = "python -m pytest $ARGS {args}" +nwarn = "python -m pytest $ARGS -W default {args}" integration = "python -m pytest $ARGS --integration_tests=true {args}" [tool.hatch.version] diff --git a/tests/extension/test_serverextension.py b/tests/extension/test_serverextension.py index 0953504c07..95f74eab80 100644 --- a/tests/extension/test_serverextension.py +++ b/tests/extension/test_serverextension.py @@ -11,6 +11,10 @@ from jupyter_server.config_manager import BaseJSONConfigManager from jupyter_server.extension.serverextension import ( + DisableServerExtensionApp, + ListServerExtensionsApp, + ServerExtensionApp, + ToggleServerExtensionApp, _get_config_dir, toggle_server_extension_python, ) @@ -112,3 +116,21 @@ def test_load_ordered(jp_serverapp, jp_server_config): assert jp_serverapp.mockII is True, "Mock II should have been loaded" assert jp_serverapp.mockI is True, "Mock I should have been loaded" assert jp_serverapp.mock_shared == "II", "Mock II should be loaded after Mock I" + + +def test_server_extension_apps(jp_env_config_path, jp_extension_environ): + app = ToggleServerExtensionApp() + app.extra_args = "mock1" + app.start() + + app = DisableServerExtensionApp() + app.extra_args = "mock1" + app.start() + + app = ListServerExtensionsApp() + app.start() + + +def test_server_extension_app(): + app = ServerExtensionApp() + app.launch_instance(["list"]) diff --git a/tests/extension/test_utils.py b/tests/extension/test_utils.py index 49a36b20ee..00849de267 100644 --- a/tests/extension/test_utils.py +++ b/tests/extension/test_utils.py @@ -1,6 +1,10 @@ +import logging +import warnings + import pytest -from jupyter_server.extension.utils import validate_extension +from jupyter_server.extension.utils import get_loader, get_metadata, validate_extension +from tests.extension.mockextensions import mockext_sys # Use ServerApps environment because it monkeypatches # jupyter_core.paths and provides a config directory @@ -17,3 +21,19 @@ def test_validate_extension(): assert validate_extension("tests.extension.mockextensions.mockext_user") # enabled at Python assert validate_extension("tests.extension.mockextensions.mockext_py") + + +def test_get_loader(): + get_loader(mockext_sys) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + assert get_loader(object()) is None + + +def test_get_metadata(): + _, ext_points = get_metadata("tests.extension.mockextensions.mockext_sys") + assert len(ext_points) + _, ext_points = get_metadata("tests", logger=logging.getLogger()) + point = ext_points[0] + assert point["module"] == "tests" + assert point["name"] == "tests" diff --git a/tests/test_files.py b/tests/test_files.py index 06f1932591..8a93557577 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -10,6 +10,16 @@ from .utils import expected_http_error +@pytest.fixture( + params=[ + "jupyter_server.files.handlers.FilesHandler", + "jupyter_server.base.handlers.AuthenticatedFileHandler", + ] +) +def jp_argv(request): + return ["--ContentsManager.files_handler_class=" + request.param] + + @pytest.fixture( params=[ [False, ["å b"]], @@ -33,6 +43,15 @@ async def fetch_expect_404(jp_fetch, *path_parts): assert expected_http_error(e, 404), [path_parts, e] +async def test_file_types(jp_fetch, jp_root_dir): + path = Path(jp_root_dir, "test") + path.mkdir(parents=True, exist_ok=True) + foos = ["foo.tar.gz", "foo.bz", "foo.foo"] + for foo in foos: + (path / foo).write_text(foo) + await fetch_expect_200(jp_fetch, "test", foo) + + async def test_hidden_files(jp_fetch, jp_serverapp, jp_root_dir, maybe_hidden): is_hidden, path_parts = maybe_hidden path = Path(jp_root_dir, *path_parts) @@ -85,7 +104,7 @@ async def test_contents_manager(jp_fetch, jp_serverapp, jp_root_dir): r = await jp_fetch("files/test.txt", method="GET") assert r.code == 200 - assert r.headers["content-type"] == "text/plain; charset=UTF-8" + assert "text/plain" in r.headers["content-type"] assert r.body.decode() == "foobar" diff --git a/tests/test_utils.py b/tests/test_utils.py index d3e168ff60..53e3acbc05 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,6 +2,7 @@ import socket import subprocess import sys +import uuid import warnings from pathlib import Path from unittest.mock import patch @@ -116,7 +117,9 @@ async def foo(): @pytest.mark.skipif(os.name != "posix", reason="Requires unix sockets") def test_unix_socket_in_use(tmp_path): root_tmp_dir = Path("/tmp").resolve() - server_address = os.path.join(root_tmp_dir, os.path.basename(tmp_path)) + server_address = os.path.join(root_tmp_dir, uuid.uuid4().hex) + if os.path.exists(server_address): + os.remove(server_address) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(server_address) sock.listen(0)