mirror of
https://github.com/jupyter/docker-stacks.git
synced 2025-10-08 18:44:06 +00:00
Fix all typing issues
This commit is contained in:
@@ -17,6 +17,13 @@ repos:
|
|||||||
- id: black
|
- id: black
|
||||||
args: [--target-version=py39]
|
args: [--target-version=py39]
|
||||||
|
|
||||||
|
# Check python code static typing
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v0.931
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
additional_dependencies: ["pytest", "types-requests", "types-tabulate"]
|
||||||
|
|
||||||
# Autoformat: YAML, JSON, Markdown, etc.
|
# Autoformat: YAML, JSON, Markdown, etc.
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
rev: v2.5.1
|
rev: v2.5.1
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
# mypy: ignore-errors
|
||||||
from jupyter_core.paths import jupyter_data_dir
|
from jupyter_core.paths import jupyter_data_dir
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import errno
|
|
||||||
import stat
|
import stat
|
||||||
|
|
||||||
|
|
||||||
c = get_config() # noqa: F821
|
c = get_config() # noqa: F821
|
||||||
c.NotebookApp.ip = "0.0.0.0"
|
c.NotebookApp.ip = "0.0.0.0"
|
||||||
c.NotebookApp.port = 8888
|
c.NotebookApp.port = 8888
|
||||||
@@ -16,28 +16,21 @@ c.NotebookApp.open_browser = False
|
|||||||
c.FileContentsManager.delete_to_trash = False
|
c.FileContentsManager.delete_to_trash = False
|
||||||
|
|
||||||
# Generate a self-signed certificate
|
# Generate a self-signed certificate
|
||||||
|
OPENSSL_CONFIG = """\
|
||||||
|
[req]
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
[req_distinguished_name]
|
||||||
|
"""
|
||||||
if "GEN_CERT" in os.environ:
|
if "GEN_CERT" in os.environ:
|
||||||
dir_name = jupyter_data_dir()
|
dir_name = jupyter_data_dir()
|
||||||
pem_file = os.path.join(dir_name, "notebook.pem")
|
pem_file = os.path.join(dir_name, "notebook.pem")
|
||||||
try:
|
os.makedirs(dir_name, exist_ok=True)
|
||||||
os.makedirs(dir_name)
|
|
||||||
except OSError as exc: # Python >2.5
|
|
||||||
if exc.errno == errno.EEXIST and os.path.isdir(dir_name):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Generate an openssl.cnf file to set the distinguished name
|
# Generate an openssl.cnf file to set the distinguished name
|
||||||
cnf_file = os.path.join(os.getenv("CONDA_DIR", "/usr/lib"), "ssl", "openssl.cnf")
|
cnf_file = os.path.join(os.getenv("CONDA_DIR", "/usr/lib"), "ssl", "openssl.cnf")
|
||||||
if not os.path.isfile(cnf_file):
|
if not os.path.isfile(cnf_file):
|
||||||
with open(cnf_file, "w") as fh:
|
with open(cnf_file, "w") as fh:
|
||||||
fh.write(
|
fh.write(OPENSSL_CONFIG)
|
||||||
"""\
|
|
||||||
[req]
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
[req_distinguished_name]
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate a certificate if one doesn't exist on disk
|
# Generate a certificate if one doesn't exist on disk
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
|
@@ -4,7 +4,7 @@ import pathlib
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
@@ -303,6 +303,6 @@ def test_jupyter_env_vars_to_unset_as_root(
|
|||||||
"-c",
|
"-c",
|
||||||
"echo I like $FRUIT and ${SECRET_FRUIT:-stuff}, and love ${SECRET_ANIMAL:-to keep secrets}!",
|
"echo I like $FRUIT and ${SECRET_FRUIT:-stuff}, and love ${SECRET_ANIMAL:-to keep secrets}!",
|
||||||
],
|
],
|
||||||
**root_args,
|
**root_args, # type: ignore
|
||||||
)
|
)
|
||||||
assert "I like bananas and stuff, and love to keep secrets!" in logs
|
assert "I like bananas and stuff, and love to keep secrets!" in logs
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from packaging import version
|
from packaging import version # type: ignore
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
25
conftest.py
25
conftest.py
@@ -2,11 +2,11 @@
|
|||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import typing
|
from typing import Any, Optional
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
from docker.models.containers import Container
|
from docker.models.containers import Container
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from requests.packages.urllib3.util.retry import Retry
|
from requests.packages.urllib3.util.retry import Retry
|
||||||
@@ -35,7 +35,7 @@ def docker_client() -> docker.DockerClient:
|
|||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def image_name() -> str:
|
def image_name() -> str:
|
||||||
"""Image name to test"""
|
"""Image name to test"""
|
||||||
return os.getenv("TEST_IMAGE")
|
return os.environ["TEST_IMAGE"]
|
||||||
|
|
||||||
|
|
||||||
class TrackedContainer:
|
class TrackedContainer:
|
||||||
@@ -56,14 +56,14 @@ class TrackedContainer:
|
|||||||
self,
|
self,
|
||||||
docker_client: docker.DockerClient,
|
docker_client: docker.DockerClient,
|
||||||
image_name: str,
|
image_name: str,
|
||||||
**kwargs: typing.Any,
|
**kwargs: Any,
|
||||||
):
|
):
|
||||||
self.container = None
|
self.container: Optional[Container] = None
|
||||||
self.docker_client = docker_client
|
self.docker_client: docker.DockerClient = docker_client
|
||||||
self.image_name = image_name
|
self.image_name: str = image_name
|
||||||
self.kwargs = kwargs
|
self.kwargs: Any = kwargs
|
||||||
|
|
||||||
def run_detached(self, **kwargs: typing.Any) -> Container:
|
def run_detached(self, **kwargs: Any) -> Container:
|
||||||
"""Runs a docker container using the preconfigured image name
|
"""Runs a docker container using the preconfigured image name
|
||||||
and a mix of the preconfigured container options and those passed
|
and a mix of the preconfigured container options and those passed
|
||||||
to this method.
|
to this method.
|
||||||
@@ -94,11 +94,12 @@ class TrackedContainer:
|
|||||||
timeout: int,
|
timeout: int,
|
||||||
no_warnings: bool = True,
|
no_warnings: bool = True,
|
||||||
no_errors: bool = True,
|
no_errors: bool = True,
|
||||||
**kwargs: typing.Any,
|
**kwargs: Any,
|
||||||
) -> str:
|
) -> str:
|
||||||
running_container = self.run_detached(**kwargs)
|
running_container = self.run_detached(**kwargs)
|
||||||
rv = running_container.wait(timeout=timeout)
|
rv = running_container.wait(timeout=timeout)
|
||||||
logs = running_container.logs().decode("utf-8")
|
logs = running_container.logs().decode("utf-8")
|
||||||
|
assert isinstance(logs, str)
|
||||||
LOGGER.debug(logs)
|
LOGGER.debug(logs)
|
||||||
if no_warnings:
|
if no_warnings:
|
||||||
assert not self.get_warnings(logs)
|
assert not self.get_warnings(logs)
|
||||||
@@ -119,14 +120,14 @@ class TrackedContainer:
|
|||||||
def _lines_starting_with(logs: str, pattern: str) -> list[str]:
|
def _lines_starting_with(logs: str, pattern: str) -> list[str]:
|
||||||
return [line for line in logs.splitlines() if line.startswith(pattern)]
|
return [line for line in logs.splitlines() if line.startswith(pattern)]
|
||||||
|
|
||||||
def remove(self):
|
def remove(self) -> None:
|
||||||
"""Kills and removes the tracked docker container."""
|
"""Kills and removes the tracked docker container."""
|
||||||
if self.container:
|
if self.container:
|
||||||
self.container.remove(force=True)
|
self.container.remove(force=True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def container(docker_client: docker.DockerClient, image_name: str):
|
def container(docker_client: docker.DockerClient, image_name: str) -> Container:
|
||||||
"""Notebook container with initial configuration appropriate for testing
|
"""Notebook container with initial configuration appropriate for testing
|
||||||
(e.g., HTTP port exposed to the host for HTTP calls).
|
(e.g., HTTP port exposed to the host for HTTP calls).
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
26
mypy.ini
Normal file
26
mypy.ini
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[mypy]
|
||||||
|
python_version = 3.9
|
||||||
|
follow_imports = normal
|
||||||
|
strict = False
|
||||||
|
no_incremental = True
|
||||||
|
|
||||||
|
[mypy-docker.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-matplotlib.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-packaging.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-pandas.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-plumbum.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-pyspark.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-tensorflow.*]
|
||||||
|
ignore_missing_imports = True
|
@@ -10,4 +10,5 @@
|
|||||||
# Attempt to capture and forward low-level output, e.g. produced by Extension
|
# Attempt to capture and forward low-level output, e.g. produced by Extension
|
||||||
# libraries.
|
# libraries.
|
||||||
# Default: True
|
# Default: True
|
||||||
|
# type:ignore
|
||||||
c.IPKernelApp.capture_fd_output = False # noqa: F821
|
c.IPKernelApp.capture_fd_output = False # noqa: F821
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
@@ -29,7 +29,7 @@ THIS_DIR = Path(__file__).parent.resolve()
|
|||||||
)
|
)
|
||||||
def test_matplotlib(
|
def test_matplotlib(
|
||||||
container: TrackedContainer, test_file: str, expected_file: str, description: str
|
container: TrackedContainer, test_file: str, expected_file: str, description: str
|
||||||
):
|
) -> None:
|
||||||
"""Various tests performed on matplotlib
|
"""Various tests performed on matplotlib
|
||||||
|
|
||||||
- Test that matplotlib is able to plot a graph and write it as an image
|
- Test that matplotlib is able to plot a graph and write it as an image
|
||||||
|
@@ -5,6 +5,7 @@ import argparse
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from docker.models.containers import Container
|
||||||
from .docker_runner import DockerRunner
|
from .docker_runner import DockerRunner
|
||||||
from .get_taggers_and_manifests import get_taggers_and_manifests
|
from .get_taggers_and_manifests import get_taggers_and_manifests
|
||||||
from .git_helper import GitHelper
|
from .git_helper import GitHelper
|
||||||
@@ -55,9 +56,9 @@ def create_manifest_file(
|
|||||||
owner: str,
|
owner: str,
|
||||||
wiki_path: str,
|
wiki_path: str,
|
||||||
manifests: list[ManifestInterface],
|
manifests: list[ManifestInterface],
|
||||||
container,
|
container: Container,
|
||||||
) -> None:
|
) -> None:
|
||||||
manifest_names = [manifest.__name__ for manifest in manifests]
|
manifest_names = [manifest.__class__.__name__ for manifest in manifests]
|
||||||
LOGGER.info(f"Using manifests: {manifest_names}")
|
LOGGER.info(f"Using manifests: {manifest_names}")
|
||||||
|
|
||||||
commit_hash_tag = GitHelper.commit_hash_tag()
|
commit_hash_tag = GitHelper.commit_hash_tag()
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from types import TracebackType
|
||||||
import docker
|
import docker
|
||||||
from docker.models.containers import Container
|
from docker.models.containers import Container
|
||||||
import logging
|
import logging
|
||||||
@@ -13,7 +14,7 @@ class DockerRunner:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
image_name: str,
|
image_name: str,
|
||||||
docker_client=docker.from_env(),
|
docker_client: docker.DockerClient = docker.from_env(),
|
||||||
command: str = "sleep infinity",
|
command: str = "sleep infinity",
|
||||||
):
|
):
|
||||||
self.container: Optional[Container] = None
|
self.container: Optional[Container] = None
|
||||||
@@ -31,7 +32,13 @@ class DockerRunner:
|
|||||||
LOGGER.info(f"Container {self.container.name} created")
|
LOGGER.info(f"Container {self.container.name} created")
|
||||||
return self.container
|
return self.container
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
def __exit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> None:
|
||||||
|
assert self.container is not None
|
||||||
LOGGER.info(f"Removing container {self.container.name} ...")
|
LOGGER.info(f"Removing container {self.container.name} ...")
|
||||||
if self.container:
|
if self.container:
|
||||||
self.container.remove(force=True)
|
self.container.remove(force=True)
|
||||||
@@ -44,6 +51,7 @@ class DockerRunner:
|
|||||||
LOGGER.info(f"Running cmd: '{cmd}' on container: {container}")
|
LOGGER.info(f"Running cmd: '{cmd}' on container: {container}")
|
||||||
out = container.exec_run(cmd)
|
out = container.exec_run(cmd)
|
||||||
result = out.output.decode("utf-8").rstrip()
|
result = out.output.decode("utf-8").rstrip()
|
||||||
|
assert isinstance(result, str)
|
||||||
if print_result:
|
if print_result:
|
||||||
LOGGER.info(f"Command result: {result}")
|
LOGGER.info(f"Command result: {result}")
|
||||||
assert out.exit_code == 0, f"Command: {cmd} failed"
|
assert out.exit_code == 0, f"Command: {cmd} failed"
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
from typing import Optional
|
||||||
from .images_hierarchy import ALL_IMAGES
|
from .images_hierarchy import ALL_IMAGES
|
||||||
from .manifests import ManifestInterface
|
from .manifests import ManifestInterface
|
||||||
from .taggers import TaggerInterface
|
from .taggers import TaggerInterface
|
||||||
|
|
||||||
|
|
||||||
def get_taggers_and_manifests(
|
def get_taggers_and_manifests(
|
||||||
short_image_name: str,
|
short_image_name: Optional[str],
|
||||||
) -> tuple[list[TaggerInterface], list[ManifestInterface]]:
|
) -> tuple[list[TaggerInterface], list[ManifestInterface]]:
|
||||||
taggers: list[TaggerInterface] = []
|
if short_image_name is None:
|
||||||
manifests: list[ManifestInterface] = []
|
return [[], []] # type: ignore
|
||||||
while short_image_name is not None:
|
|
||||||
image_description = ALL_IMAGES[short_image_name]
|
|
||||||
|
|
||||||
taggers = image_description.taggers + taggers
|
image_description = ALL_IMAGES[short_image_name]
|
||||||
manifests = image_description.manifests + manifests
|
parent_taggers, parent_manifests = get_taggers_and_manifests(
|
||||||
|
image_description.parent_image
|
||||||
short_image_name = image_description.parent_image
|
)
|
||||||
return taggers, manifests
|
return (
|
||||||
|
parent_taggers + image_description.taggers,
|
||||||
|
parent_manifests + image_description.manifests,
|
||||||
|
)
|
||||||
|
@@ -7,7 +7,7 @@ from plumbum.cmd import git
|
|||||||
class GitHelper:
|
class GitHelper:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def commit_hash() -> str:
|
def commit_hash() -> str:
|
||||||
return git["rev-parse", "HEAD"]().strip()
|
return git["rev-parse", "HEAD"]().strip() # type: ignore
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def commit_hash_tag() -> str:
|
def commit_hash_tag() -> str:
|
||||||
@@ -15,7 +15,7 @@ class GitHelper:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def commit_message() -> str:
|
def commit_message() -> str:
|
||||||
return git["log", -1, "--pretty=%B"]().strip()
|
return git["log", -1, "--pretty=%B"]().strip() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def github_set_env(env_name, env_value):
|
def github_set_env(env_name: str, env_value: str) -> None:
|
||||||
if not os.environ.get("GITHUB_ACTIONS") or not os.environ.get("GITHUB_ENV"):
|
if not os.environ.get("GITHUB_ACTIONS") or not os.environ.get("GITHUB_ENV"):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@@ -39,39 +39,39 @@ ALL_IMAGES = {
|
|||||||
"base-notebook": ImageDescription(
|
"base-notebook": ImageDescription(
|
||||||
parent_image=None,
|
parent_image=None,
|
||||||
taggers=[
|
taggers=[
|
||||||
SHATagger,
|
SHATagger(),
|
||||||
DateTagger,
|
DateTagger(),
|
||||||
UbuntuVersionTagger,
|
UbuntuVersionTagger(),
|
||||||
PythonVersionTagger,
|
PythonVersionTagger(),
|
||||||
JupyterNotebookVersionTagger,
|
JupyterNotebookVersionTagger(),
|
||||||
JupyterLabVersionTagger,
|
JupyterLabVersionTagger(),
|
||||||
JupyterHubVersionTagger,
|
JupyterHubVersionTagger(),
|
||||||
],
|
],
|
||||||
manifests=[CondaEnvironmentManifest, AptPackagesManifest],
|
manifests=[CondaEnvironmentManifest(), AptPackagesManifest()],
|
||||||
),
|
),
|
||||||
"minimal-notebook": ImageDescription(parent_image="base-notebook"),
|
"minimal-notebook": ImageDescription(parent_image="base-notebook"),
|
||||||
"scipy-notebook": ImageDescription(parent_image="minimal-notebook"),
|
"scipy-notebook": ImageDescription(parent_image="minimal-notebook"),
|
||||||
"r-notebook": ImageDescription(
|
"r-notebook": ImageDescription(
|
||||||
parent_image="minimal-notebook",
|
parent_image="minimal-notebook",
|
||||||
taggers=[RVersionTagger],
|
taggers=[RVersionTagger()],
|
||||||
manifests=[RPackagesManifest],
|
manifests=[RPackagesManifest()],
|
||||||
),
|
),
|
||||||
"tensorflow-notebook": ImageDescription(
|
"tensorflow-notebook": ImageDescription(
|
||||||
parent_image="scipy-notebook", taggers=[TensorflowVersionTagger]
|
parent_image="scipy-notebook", taggers=[TensorflowVersionTagger()]
|
||||||
),
|
),
|
||||||
"datascience-notebook": ImageDescription(
|
"datascience-notebook": ImageDescription(
|
||||||
parent_image="scipy-notebook",
|
parent_image="scipy-notebook",
|
||||||
taggers=[RVersionTagger, JuliaVersionTagger],
|
taggers=[RVersionTagger(), JuliaVersionTagger()],
|
||||||
manifests=[RPackagesManifest, JuliaPackagesManifest],
|
manifests=[RPackagesManifest(), JuliaPackagesManifest()],
|
||||||
),
|
),
|
||||||
"pyspark-notebook": ImageDescription(
|
"pyspark-notebook": ImageDescription(
|
||||||
parent_image="scipy-notebook",
|
parent_image="scipy-notebook",
|
||||||
taggers=[SparkVersionTagger, HadoopVersionTagger, JavaVersionTagger],
|
taggers=[SparkVersionTagger(), HadoopVersionTagger(), JavaVersionTagger()],
|
||||||
manifests=[SparkInfoManifest],
|
manifests=[SparkInfoManifest()],
|
||||||
),
|
),
|
||||||
"all-spark-notebook": ImageDescription(
|
"all-spark-notebook": ImageDescription(
|
||||||
parent_image="pyspark-notebook",
|
parent_image="pyspark-notebook",
|
||||||
taggers=[RVersionTagger],
|
taggers=[RVersionTagger()],
|
||||||
manifests=[RPackagesManifest],
|
manifests=[RPackagesManifest()],
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
from plumbum.cmd import docker
|
from plumbum.cmd import docker
|
||||||
|
from docker.models.containers import Container
|
||||||
from .docker_runner import DockerRunner
|
from .docker_runner import DockerRunner
|
||||||
from .git_helper import GitHelper
|
from .git_helper import GitHelper
|
||||||
|
|
||||||
|
|
||||||
def quoted_output(container, cmd: str) -> str:
|
def quoted_output(container: Container, cmd: str) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"```",
|
"```",
|
||||||
@@ -50,13 +51,13 @@ class ManifestInterface:
|
|||||||
"""Common interface for all manifests"""
|
"""Common interface for all manifests"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown_piece(container) -> str:
|
def markdown_piece(container: Container) -> str:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class CondaEnvironmentManifest(ManifestInterface):
|
class CondaEnvironmentManifest(ManifestInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown_piece(container) -> str:
|
def markdown_piece(container: Container) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"## Python Packages",
|
"## Python Packages",
|
||||||
@@ -72,7 +73,7 @@ class CondaEnvironmentManifest(ManifestInterface):
|
|||||||
|
|
||||||
class AptPackagesManifest(ManifestInterface):
|
class AptPackagesManifest(ManifestInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown_piece(container) -> str:
|
def markdown_piece(container: Container) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"## Apt Packages",
|
"## Apt Packages",
|
||||||
@@ -84,7 +85,7 @@ class AptPackagesManifest(ManifestInterface):
|
|||||||
|
|
||||||
class RPackagesManifest(ManifestInterface):
|
class RPackagesManifest(ManifestInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown_piece(container) -> str:
|
def markdown_piece(container: Container) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"## R Packages",
|
"## R Packages",
|
||||||
@@ -101,7 +102,7 @@ class RPackagesManifest(ManifestInterface):
|
|||||||
|
|
||||||
class JuliaPackagesManifest(ManifestInterface):
|
class JuliaPackagesManifest(ManifestInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown_piece(container) -> str:
|
def markdown_piece(container: Container) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"## Julia Packages",
|
"## Julia Packages",
|
||||||
@@ -118,7 +119,7 @@ class JuliaPackagesManifest(ManifestInterface):
|
|||||||
|
|
||||||
class SparkInfoManifest(ManifestInterface):
|
class SparkInfoManifest(ManifestInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def markdown_piece(container) -> str:
|
def markdown_piece(container: Container) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"## Apache Spark",
|
"## Apache Spark",
|
||||||
|
@@ -28,11 +28,11 @@ def tag_image(short_image_name: str, owner: str) -> None:
|
|||||||
with DockerRunner(image) as container:
|
with DockerRunner(image) as container:
|
||||||
tags = []
|
tags = []
|
||||||
for tagger in taggers:
|
for tagger in taggers:
|
||||||
tagger_name = tagger.__name__
|
tagger_name = tagger.__class__.__name__
|
||||||
tag_value = tagger.tag_value(container)
|
tag_value = tagger.tag_value(container)
|
||||||
tags.append(tag_value)
|
tags.append(tag_value)
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
f"Applying tag tagger_name: {tagger_name} tag_value: {tag_value}"
|
f"Applying tag, tagger_name: {tagger_name} tag_value: {tag_value}"
|
||||||
)
|
)
|
||||||
docker["tag", image, f"{owner}/{short_image_name}:{tag_value}"]()
|
docker["tag", image, f"{owner}/{short_image_name}:{tag_value}"]()
|
||||||
|
|
||||||
|
@@ -1,15 +1,16 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from docker.models.containers import Container
|
||||||
from .git_helper import GitHelper
|
from .git_helper import GitHelper
|
||||||
from .docker_runner import DockerRunner
|
from .docker_runner import DockerRunner
|
||||||
|
|
||||||
|
|
||||||
def _get_program_version(container, program: str) -> str:
|
def _get_program_version(container: Container, program: str) -> str:
|
||||||
return DockerRunner.run_simple_command(container, cmd=f"{program} --version")
|
return DockerRunner.run_simple_command(container, cmd=f"{program} --version")
|
||||||
|
|
||||||
|
|
||||||
def _get_env_variable(container, variable: str) -> str:
|
def _get_env_variable(container: Container, variable: str) -> str:
|
||||||
env = DockerRunner.run_simple_command(
|
env = DockerRunner.run_simple_command(
|
||||||
container,
|
container,
|
||||||
cmd="env",
|
cmd="env",
|
||||||
@@ -21,7 +22,7 @@ def _get_env_variable(container, variable: str) -> str:
|
|||||||
raise KeyError(variable)
|
raise KeyError(variable)
|
||||||
|
|
||||||
|
|
||||||
def _get_pip_package_version(container, package: str) -> str:
|
def _get_pip_package_version(container: Container, package: str) -> str:
|
||||||
VERSION_PREFIX = "Version: "
|
VERSION_PREFIX = "Version: "
|
||||||
package_info = DockerRunner.run_simple_command(
|
package_info = DockerRunner.run_simple_command(
|
||||||
container,
|
container,
|
||||||
@@ -37,25 +38,25 @@ class TaggerInterface:
|
|||||||
"""Common interface for all taggers"""
|
"""Common interface for all taggers"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class SHATagger(TaggerInterface):
|
class SHATagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return GitHelper.commit_hash_tag()
|
return GitHelper.commit_hash_tag()
|
||||||
|
|
||||||
|
|
||||||
class DateTagger(TaggerInterface):
|
class DateTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return datetime.utcnow().strftime("%Y-%m-%d")
|
return datetime.utcnow().strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
|
||||||
class UbuntuVersionTagger(TaggerInterface):
|
class UbuntuVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
os_release = DockerRunner.run_simple_command(
|
os_release = DockerRunner.run_simple_command(
|
||||||
container,
|
container,
|
||||||
"cat /etc/os-release",
|
"cat /etc/os-release",
|
||||||
@@ -63,63 +64,64 @@ class UbuntuVersionTagger(TaggerInterface):
|
|||||||
for line in os_release:
|
for line in os_release:
|
||||||
if line.startswith("VERSION_ID"):
|
if line.startswith("VERSION_ID"):
|
||||||
return "ubuntu-" + line.split("=")[1].strip('"')
|
return "ubuntu-" + line.split("=")[1].strip('"')
|
||||||
|
raise RuntimeError(f"did not find ubuntu version in: {os_release}")
|
||||||
|
|
||||||
|
|
||||||
class PythonVersionTagger(TaggerInterface):
|
class PythonVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "python-" + _get_program_version(container, "python").split()[1]
|
return "python-" + _get_program_version(container, "python").split()[1]
|
||||||
|
|
||||||
|
|
||||||
class JupyterNotebookVersionTagger(TaggerInterface):
|
class JupyterNotebookVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "notebook-" + _get_program_version(container, "jupyter-notebook")
|
return "notebook-" + _get_program_version(container, "jupyter-notebook")
|
||||||
|
|
||||||
|
|
||||||
class JupyterLabVersionTagger(TaggerInterface):
|
class JupyterLabVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "lab-" + _get_program_version(container, "jupyter-lab")
|
return "lab-" + _get_program_version(container, "jupyter-lab")
|
||||||
|
|
||||||
|
|
||||||
class JupyterHubVersionTagger(TaggerInterface):
|
class JupyterHubVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "hub-" + _get_program_version(container, "jupyterhub")
|
return "hub-" + _get_program_version(container, "jupyterhub")
|
||||||
|
|
||||||
|
|
||||||
class RVersionTagger(TaggerInterface):
|
class RVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "r-" + _get_program_version(container, "R").split()[2]
|
return "r-" + _get_program_version(container, "R").split()[2]
|
||||||
|
|
||||||
|
|
||||||
class TensorflowVersionTagger(TaggerInterface):
|
class TensorflowVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "tensorflow-" + _get_pip_package_version(container, "tensorflow")
|
return "tensorflow-" + _get_pip_package_version(container, "tensorflow")
|
||||||
|
|
||||||
|
|
||||||
class JuliaVersionTagger(TaggerInterface):
|
class JuliaVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "julia-" + _get_program_version(container, "julia").split()[2]
|
return "julia-" + _get_program_version(container, "julia").split()[2]
|
||||||
|
|
||||||
|
|
||||||
class SparkVersionTagger(TaggerInterface):
|
class SparkVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "spark-" + _get_env_variable(container, "APACHE_SPARK_VERSION")
|
return "spark-" + _get_env_variable(container, "APACHE_SPARK_VERSION")
|
||||||
|
|
||||||
|
|
||||||
class HadoopVersionTagger(TaggerInterface):
|
class HadoopVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "hadoop-" + _get_env_variable(container, "HADOOP_VERSION")
|
return "hadoop-" + _get_env_variable(container, "HADOOP_VERSION")
|
||||||
|
|
||||||
|
|
||||||
class JavaVersionTagger(TaggerInterface):
|
class JavaVersionTagger(TaggerInterface):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tag_value(container) -> str:
|
def tag_value(container: Container) -> str:
|
||||||
return "java-" + _get_program_version(container, "java").split()[1]
|
return "java-" + _get_program_version(container, "java").split()[1]
|
||||||
|
@@ -27,7 +27,8 @@ from collections import defaultdict
|
|||||||
from itertools import chain
|
from itertools import chain
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
from docker.models.containers import Container
|
||||||
|
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
@@ -40,14 +41,16 @@ class CondaPackageHelper:
|
|||||||
"""Conda package helper permitting to get information about packages"""
|
"""Conda package helper permitting to get information about packages"""
|
||||||
|
|
||||||
def __init__(self, container: TrackedContainer):
|
def __init__(self, container: TrackedContainer):
|
||||||
self.running_container = CondaPackageHelper.start_container(container)
|
self.running_container: Container = CondaPackageHelper.start_container(
|
||||||
|
container
|
||||||
|
)
|
||||||
self.requested: Optional[dict[str, set[str]]] = None
|
self.requested: Optional[dict[str, set[str]]] = None
|
||||||
self.installed: Optional[dict[str, set[str]]] = None
|
self.installed: Optional[dict[str, set[str]]] = None
|
||||||
self.available: Optional[dict[str, set[str]]] = None
|
self.available: Optional[dict[str, set[str]]] = None
|
||||||
self.comparison: list[dict[str, str]] = []
|
self.comparison: list[dict[str, str]] = []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def start_container(container: TrackedContainer):
|
def start_container(container: TrackedContainer) -> Container:
|
||||||
"""Start the TrackedContainer and return an instance of a running container"""
|
"""Start the TrackedContainer and return an instance of a running container"""
|
||||||
LOGGER.info(f"Starting container {container.image_name} ...")
|
LOGGER.info(f"Starting container {container.image_name} ...")
|
||||||
return container.run_detached(
|
return container.run_detached(
|
||||||
@@ -85,13 +88,13 @@ class CondaPackageHelper:
|
|||||||
)
|
)
|
||||||
return self.requested
|
return self.requested
|
||||||
|
|
||||||
def _execute_command(self, command):
|
def _execute_command(self, command: list[str]) -> str:
|
||||||
"""Execute a command on a running container"""
|
"""Execute a command on a running container"""
|
||||||
rc = self.running_container.exec_run(command)
|
rc = self.running_container.exec_run(command)
|
||||||
return rc.output.decode("utf-8")
|
return rc.output.decode("utf-8") # type: ignore
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _packages_from_json(env_export) -> dict[str, set[str]]:
|
def _packages_from_json(env_export: str) -> dict[str, set[str]]:
|
||||||
"""Extract packages and versions from the lines returned by the list of specifications"""
|
"""Extract packages and versions from the lines returned by the list of specifications"""
|
||||||
# dependencies = filter(lambda x: isinstance(x, str), json.loads(env_export).get("dependencies"))
|
# dependencies = filter(lambda x: isinstance(x, str), json.loads(env_export).get("dependencies"))
|
||||||
dependencies = json.loads(env_export).get("dependencies")
|
dependencies = json.loads(env_export).get("dependencies")
|
||||||
@@ -114,7 +117,7 @@ class CondaPackageHelper:
|
|||||||
packages_dict[package] = version
|
packages_dict[package] = version
|
||||||
return packages_dict
|
return packages_dict
|
||||||
|
|
||||||
def available_packages(self):
|
def available_packages(self) -> dict[str, set[str]]:
|
||||||
"""Return the available packages"""
|
"""Return the available packages"""
|
||||||
if self.available is None:
|
if self.available is None:
|
||||||
LOGGER.info("Grabing the list of available packages (can take a while) ...")
|
LOGGER.info("Grabing the list of available packages (can take a while) ...")
|
||||||
@@ -125,11 +128,13 @@ class CondaPackageHelper:
|
|||||||
return self.available
|
return self.available
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_available(lines):
|
def _extract_available(lines: str) -> dict[str, set[str]]:
|
||||||
"""Extract packages and versions from the lines returned by the list of packages"""
|
"""Extract packages and versions from the lines returned by the list of packages"""
|
||||||
ddict = defaultdict(set)
|
ddict = defaultdict(set)
|
||||||
for line in lines.splitlines()[2:]:
|
for line in lines.splitlines()[2:]:
|
||||||
pkg, version = re.match(r"^(\S+)\s+(\S+)", line, re.MULTILINE).groups()
|
match = re.match(r"^(\S+)\s+(\S+)", line, re.MULTILINE)
|
||||||
|
assert match is not None
|
||||||
|
pkg, version = match.groups()
|
||||||
ddict[pkg].add(version)
|
ddict[pkg].add(version)
|
||||||
return ddict
|
return ddict
|
||||||
|
|
||||||
@@ -162,11 +167,11 @@ class CondaPackageHelper:
|
|||||||
return self.comparison
|
return self.comparison
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def semantic_cmp(version_string: str):
|
def semantic_cmp(version_string: str) -> Any:
|
||||||
"""Manage semantic versioning for comparison"""
|
"""Manage semantic versioning for comparison"""
|
||||||
|
|
||||||
def mysplit(string):
|
def mysplit(string: str) -> list[Any]:
|
||||||
def version_substrs(x):
|
def version_substrs(x: str) -> list[str]:
|
||||||
return re.findall(r"([A-z]+|\d+)", x)
|
return re.findall(r"([A-z]+|\d+)", x)
|
||||||
|
|
||||||
return list(chain(map(version_substrs, string.split("."))))
|
return list(chain(map(version_substrs, string.split("."))))
|
||||||
@@ -189,7 +194,9 @@ class CondaPackageHelper:
|
|||||||
|
|
||||||
def get_outdated_summary(self, requested_only: bool = True) -> str:
|
def get_outdated_summary(self, requested_only: bool = True) -> str:
|
||||||
"""Return a summary of outdated packages"""
|
"""Return a summary of outdated packages"""
|
||||||
nb_packages = len(self.requested if requested_only else self.installed)
|
packages = self.requested if requested_only else self.installed
|
||||||
|
assert packages is not None
|
||||||
|
nb_packages = len(packages)
|
||||||
nb_updatable = len(self.comparison)
|
nb_updatable = len(self.comparison)
|
||||||
updatable_ratio = nb_updatable / nb_packages
|
updatable_ratio = nb_updatable / nb_packages
|
||||||
return f"{nb_updatable}/{nb_packages} ({updatable_ratio:.0%}) packages could be updated"
|
return f"{nb_updatable}/{nb_packages} ({updatable_ratio:.0%}) packages could be updated"
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
|
||||||
from package_helper import CondaPackageHelper
|
from package_helper import CondaPackageHelper
|
||||||
@@ -12,7 +12,9 @@ LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.info
|
@pytest.mark.info
|
||||||
def test_outdated_packages(container: TrackedContainer, requested_only: bool = True):
|
def test_outdated_packages(
|
||||||
|
container: TrackedContainer, requested_only: bool = True
|
||||||
|
) -> None:
|
||||||
"""Getting the list of updatable packages"""
|
"""Getting the list of updatable packages"""
|
||||||
LOGGER.info(f"Checking outdated packages in {container.image_name} ...")
|
LOGGER.info(f"Checking outdated packages in {container.image_name} ...")
|
||||||
pkg_helper = CondaPackageHelper(container)
|
pkg_helper = CondaPackageHelper(container)
|
||||||
|
@@ -37,8 +37,9 @@ Example:
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest # type: ignore
|
||||||
from conftest import TrackedContainer
|
from conftest import TrackedContainer
|
||||||
|
from typing import Callable, Iterable
|
||||||
|
|
||||||
from package_helper import CondaPackageHelper
|
from package_helper import CondaPackageHelper
|
||||||
|
|
||||||
@@ -87,7 +88,7 @@ def packages(package_helper: CondaPackageHelper) -> dict[str, set[str]]:
|
|||||||
return package_helper.requested_packages()
|
return package_helper.requested_packages()
|
||||||
|
|
||||||
|
|
||||||
def package_map(package: str) -> str:
|
def get_package_import_name(package: str) -> str:
|
||||||
"""Perform a mapping between the python package name and the name used for the import"""
|
"""Perform a mapping between the python package name and the name used for the import"""
|
||||||
return PACKAGE_MAPPING.get(package, package)
|
return PACKAGE_MAPPING.get(package, package)
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ def _check_import_package(
|
|||||||
"""Generic function executing a command"""
|
"""Generic function executing a command"""
|
||||||
LOGGER.debug(f"Trying to import a package with [{command}] ...")
|
LOGGER.debug(f"Trying to import a package with [{command}] ...")
|
||||||
rc = package_helper.running_container.exec_run(command)
|
rc = package_helper.running_container.exec_run(command)
|
||||||
return rc.exit_code
|
return rc.exit_code # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def check_import_python_package(
|
def check_import_python_package(
|
||||||
@@ -130,10 +131,10 @@ def check_import_r_package(package_helper: CondaPackageHelper, package: str) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _import_packages(
|
def _check_import_packages(
|
||||||
package_helper: CondaPackageHelper,
|
package_helper: CondaPackageHelper,
|
||||||
filtered_packages: dict[str, set[str]],
|
filtered_packages: Iterable[str],
|
||||||
check_function,
|
check_function: Callable[[CondaPackageHelper, str], int],
|
||||||
max_failures: int,
|
max_failures: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test if packages can be imported
|
"""Test if packages can be imported
|
||||||
@@ -157,33 +158,36 @@ def _import_packages(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def r_packages(packages: dict[str, set[str]]):
|
def r_packages(packages: dict[str, set[str]]) -> Iterable[str]:
|
||||||
"""Return an iterable of R packages"""
|
"""Return an iterable of R packages"""
|
||||||
# package[2:] is to remove the leading "r-" appended on R packages
|
# package[2:] is to remove the leading "r-" appended on R packages
|
||||||
return map(
|
return map(
|
||||||
lambda package: package_map(package[2:]), filter(r_package_predicate, packages)
|
lambda package: get_package_import_name(package[2:]),
|
||||||
|
filter(r_package_predicate, packages),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_r_packages(
|
def test_r_packages(
|
||||||
package_helper: CondaPackageHelper, r_packages, max_failures: int = 0
|
package_helper: CondaPackageHelper, r_packages: Iterable[str], max_failures: int = 0
|
||||||
):
|
) -> None:
|
||||||
"""Test the import of specified R packages"""
|
"""Test the import of specified R packages"""
|
||||||
return _import_packages(
|
_check_import_packages(
|
||||||
package_helper, r_packages, check_import_r_package, max_failures
|
package_helper, r_packages, check_import_r_package, max_failures
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def python_packages(packages: dict[str, set[str]]):
|
def python_packages(packages: dict[str, set[str]]) -> Iterable[str]:
|
||||||
"""Return an iterable of Python packages"""
|
"""Return an iterable of Python packages"""
|
||||||
return map(package_map, filter(python_package_predicate, packages))
|
return map(get_package_import_name, filter(python_package_predicate, packages))
|
||||||
|
|
||||||
|
|
||||||
def test_python_packages(
|
def test_python_packages(
|
||||||
package_helper: CondaPackageHelper, python_packages, max_failures: int = 0
|
package_helper: CondaPackageHelper,
|
||||||
):
|
python_packages: Iterable[str],
|
||||||
|
max_failures: int = 0,
|
||||||
|
) -> None:
|
||||||
"""Test the import of specified python packages"""
|
"""Test the import of specified python packages"""
|
||||||
return _import_packages(
|
_check_import_packages(
|
||||||
package_helper, python_packages, check_import_python_package, max_failures
|
package_helper, python_packages, check_import_python_package, max_failures
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user