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

Add more ci checks #42

Merged
merged 2 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install enchant-2 # for spell checking
- run: hatch run docs:build

test_lint:
Expand All @@ -59,7 +63,7 @@ jobs:
run: |
hatch run typing:test
hatch run lint:style
pipx run 'validate-pyproject[all]' pyproject.toml
pipx run interrogate -v .
pipx run doc8 --max-line-length=200 --ignore-path=docs/source/other/full-config.rst

test_minimum_versions:
Expand Down
11 changes: 6 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
- id: check-case-conflict
- id: check-ast
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: requirements-txt-fixer
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-json
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: forbid-new-submodules
- id: check-builtin-literals
- id: end-of-file-fixer
- id: trailing-whitespace

- repo: https://github.com/python-jsonschema/check-jsonschema
Expand All @@ -34,7 +35,7 @@ repos:
- id: black

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.165
rev: v0.0.189
hooks:
- id: ruff
args: ["--fix"]
8 changes: 8 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
"sphinx.ext.autodoc",
]

try:
import enchant # type:ignore # noqa

extensions += ["sphinxcontrib.spelling"]
except ImportError:
pass


# Autodoc fixtures
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
Expand Down
15 changes: 13 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ docs = [
"myst_parser",
"pydata_sphinx_theme",
"Sphinx",
"sphinxcontrib-spelling"
]
client = [
"jupyter_client>=7.4.0",
Expand All @@ -52,9 +53,9 @@ test = [
"pytest-timeout"
]
lint = [
"black>=22.6.0",
"black[jupyter]==22.10.0",
"mdformat>0.7",
"ruff>=0.0.156",
"ruff==0.0.189",
]
typing = [
"mypy>=0.990"
Expand Down Expand Up @@ -216,3 +217,13 @@ unfixable = [
# B007 Loop control variable `i` not used within the loop body.
# N802 Function name `assertIn` should be lowercase
"tests/*" = ["B011", "F841", "C408", "E402", "T201", "B007", "N802"]

[tool.interrogate]
ignore-init-module=true
ignore-private=true
ignore-semiprivate=true
ignore-property-decorators=true
ignore-nested-functions=true
ignore-nested-classes=true
fail-under=100
exclude = ["docs", "tests"]
1 change: 1 addition & 0 deletions pytest_jupyter/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Version info for pytest_jupyter."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

Expand Down
6 changes: 6 additions & 0 deletions pytest_jupyter/echo_kernel.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""A simple echo kernel."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

Expand All @@ -8,6 +9,8 @@


class EchoKernel(Kernel):
"""An echo kernel."""

implementation = "Echo"
implementation_version = "1.0"
language = "echo"
Expand All @@ -22,6 +25,7 @@ class EchoKernel(Kernel):
def do_execute(
self, code, silent, store_history=True, user_expressions=None, allow_stdin=False
):
"""Execute code on the kernel."""
if not silent:
stream_content = {"name": "stdout", "text": code}
self.send_response(self.iopub_socket, "stream", stream_content)
Expand All @@ -45,6 +49,8 @@ def do_execute(


class EchoKernelApp(IPKernelApp):
"""An app for the echo kernel."""

kernel_class = EchoKernel


Expand Down
4 changes: 4 additions & 0 deletions pytest_jupyter/jupyter_client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Fixtures for use with jupyter_client and downstream."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

Expand All @@ -24,6 +25,7 @@

@pytest.fixture
def jp_zmq_context():
"""Get a zmq context."""
import zmq

ctx = zmq.asyncio.Context()
Expand All @@ -33,10 +35,12 @@ def jp_zmq_context():

@pytest.fixture
def jp_start_kernel(jp_environ, jp_asyncio_loop):
"""Get a function to a kernel and clean up resources when done."""
kms = []
kcs = []

async def inner(kernel_name=NATIVE_KERNEL_NAME, **kwargs):
"""A function used to start a kernel."""
km, kc = await start_new_async_kernel(kernel_name=kernel_name, **kwargs)
kms.append(km)
kcs.append(kc)
Expand Down
5 changes: 5 additions & 0 deletions pytest_jupyter/jupyter_core.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Fixtures for use with jupyter core and downstream."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import asyncio
Expand Down Expand Up @@ -35,6 +36,7 @@

@pytest.fixture
def jp_asyncio_loop():
"""Get an asyncio loop."""
if os.name == "nt":
asyncio.set_event_loop_policy(
asyncio.WindowsSelectorEventLoopPolicy() # type:ignore[attr-defined]
Expand All @@ -51,6 +53,7 @@ def io_loop(jp_asyncio_loop):
if tornado is not installed."""

async def get_tornado_loop():
"""Asynchronously get a tornado loop."""
try:
from tornado.ioloop import IOLoop

Expand Down Expand Up @@ -111,11 +114,13 @@ def jp_env_config_path(tmp_path):

@pytest.fixture()
def jp_kernel_dir(jp_data_dir):
"""Get the directory for kernel specs."""
return mkdir(jp_data_dir, "kernels")


@pytest.fixture
def echo_kernel_spec(jp_kernel_dir):
"""Install a kernel spec for the echo kernel."""
test_dir = Path(jp_kernel_dir) / "echo"
test_dir.mkdir(parents=True, exist_ok=True)
argv = [sys.executable, "-m", "pytest_jupyter.echo_kernel", "-f", "{connection_file}"]
Expand Down
9 changes: 9 additions & 0 deletions pytest_jupyter/jupyter_server.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Fixtures for use with jupyter server and downstream."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

Expand Down Expand Up @@ -59,6 +60,7 @@ def http_server(io_loop, http_server_port, jp_web_app):
"""Start a tornado HTTP server that listens on all available interfaces."""

async def get_server():
"""Get a server asynchronously."""
server = tornado.httpserver.HTTPServer(jp_web_app)
server.add_socket(http_server_port[0])
return server
Expand Down Expand Up @@ -385,6 +387,7 @@ def inner(nbpath):

@pytest.fixture(autouse=True)
def jp_server_cleanup(jp_asyncio_loop):
"""Automatically cleans up server resources."""
yield
app: ServerApp = ServerApp.instance()
try:
Expand Down Expand Up @@ -422,6 +425,7 @@ async def _(url, **fetch_kwargs):

@pytest.fixture
def jp_server_auth_core_resources():
"""The core auth resources for use with a server."""
modules = []
for mod_name in JUPYTER_SERVICE_HANDLERS.values():
if mod_name:
Expand All @@ -438,10 +442,13 @@ def jp_server_auth_core_resources():

@pytest.fixture
def jp_server_auth_resources(jp_server_auth_core_resources):
"""The auth resources used by the server."""
return jp_server_auth_core_resources


class _Authorizer(Authorizer):
"""A custom authorizer class for testing."""

# Set these class attributes from within a test
# to verify that they match the arguments passed
# by the REST API.
Expand Down Expand Up @@ -484,6 +491,7 @@ def normalize_url(self, path):
return path

def is_authorized(self, handler, user, action, resource):
"""Test if a request is authorized."""
# Parse Request
if isinstance(handler, WebSocketHandler):
method = "WEBSOCKET"
Expand Down Expand Up @@ -511,6 +519,7 @@ def is_authorized(self, handler, user, action, resource):

@pytest.fixture
def jp_server_authorizer(jp_server_auth_resources):
"""An authorizer for the server."""
auth_klass = _Authorizer
auth_klass._default_regex_mapping = jp_server_auth_resources
return auth_klass
15 changes: 12 additions & 3 deletions pytest_jupyter/pytest_tornasync.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Vendored fork of pytest_tornasync from
# https://github.com/eukaryote/pytest-tornasync/blob/9f1bdeec3eb5816e0183f975ca65b5f6f29fbfbb/src/pytest_tornasync/plugin.py

"""Vendored fork of pytest_tornasync from
https://github.com/eukaryote/pytest-tornasync/blob/9f1bdeec3eb5816e0183f975ca65b5f6f29fbfbb/src/pytest_tornasync/plugin.py
"""
from contextlib import closing
from inspect import iscoroutinefunction

Expand All @@ -16,12 +16,14 @@

@pytest.hookimpl(tryfirst=True)
def pytest_pycollect_makeitem(collector, name, obj):
"""Custom pytest collection hook."""
if collector.funcnamefilter(name) and iscoroutinefunction(obj):
return list(collector._genfunctions(name, obj))


@pytest.hookimpl(tryfirst=True)
def pytest_pyfunc_call(pyfuncitem):
"""Custom pytest function call hook."""
funcargs = pyfuncitem.funcargs
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}

Expand Down Expand Up @@ -53,6 +55,7 @@ def http_server_client(http_server, io_loop):
"""

async def get_client():
"""Get a client."""
return AsyncHTTPServerClient(http_server=http_server)

client = io_loop.run_sync(get_client)
Expand All @@ -61,7 +64,10 @@ async def get_client():


class AsyncHTTPServerClient(SimpleAsyncHTTPClient):
"""An async http server client."""

def initialize(self, *, http_server=None):
"""Initialize the client."""
super().initialize()
self._http_server = http_server

Expand All @@ -73,11 +79,14 @@ def fetch(self, path, **kwargs):
return super().fetch(self.get_url(path), **kwargs)

def get_protocol(self):
"""Get the protocol for the client."""
return "http"

def get_http_port(self):
"""Get a port for the client."""
for sock in self._http_server._sockets.values():
return sock.getsockname()[1]

def get_url(self, path):
"""Get the url for the client."""
return f"{self.get_protocol()}://127.0.0.1:{self.get_http_port()}{path}"
2 changes: 2 additions & 0 deletions pytest_jupyter/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Utilities for pytest-jupyter."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.


def mkdir(tmp_path, *parts):
"""Make a directory given extra path parts."""
path = tmp_path.joinpath(*parts)
if not path.exists():
path.mkdir(parents=True)
Expand Down