diff --git a/.github/integration-test.py b/.github/integration-test.py index 90875d1..b3fa090 100755 --- a/.github/integration-test.py +++ b/.github/integration-test.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 import argparse -from shutil import which +import os import subprocess import time -import os +from shutil import which def container_runtime(): diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bbda53c..f62bd40 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,6 +20,21 @@ repos: # exclude it from the pyupgrade hook that will apply f-strings etc. exclude: bootstrap/bootstrap.py + # Autoformat: Python code + - repo: https://github.com/PyCQA/autoflake + rev: v2.1.1 + hooks: + - id: autoflake + # args ref: https://github.com/PyCQA/autoflake#advanced-usage + args: + - --in-place + + # Autoformat: Python code + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + # Autoformat: Python code - repo: https://github.com/psf/black rev: 23.3.0 diff --git a/bootstrap/bootstrap.py b/bootstrap/bootstrap.py index ef9f602..59e2daf 100644 --- a/bootstrap/bootstrap.py +++ b/bootstrap/bootstrap.py @@ -42,16 +42,16 @@ Command line flags, from "bootstrap.py --help": can also pass a branch name such as 'main' or a commit hash. """ -from argparse import ArgumentParser -import os -from http.server import SimpleHTTPRequestHandler, HTTPServer +import logging import multiprocessing +import os import re +import shutil import subprocess import sys -import logging -import shutil import urllib.request +from argparse import ArgumentParser +from http.server import HTTPServer, SimpleHTTPRequestHandler progress_page_favicon_url = "https://raw.githubusercontent.com/jupyterhub/jupyterhub/main/share/jupyterhub/static/favicon.ico" progress_page_html = """ diff --git a/docs/conf.py b/docs/conf.py index 36823cc..77036f0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,7 +4,6 @@ # import datetime - # -- Project information ----------------------------------------------------- # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # diff --git a/integration-tests/test_admin_installer.py b/integration-tests/test_admin_installer.py index f039bfc..c968978 100644 --- a/integration-tests/test_admin_installer.py +++ b/integration-tests/test_admin_installer.py @@ -1,8 +1,9 @@ -from hubtraf.user import User -from hubtraf.auth.dummy import login_dummy -import pytest from functools import partial +import pytest +from hubtraf.auth.dummy import login_dummy +from hubtraf.user import User + @pytest.mark.asyncio async def test_admin_login(): diff --git a/integration-tests/test_bootstrap.py b/integration-tests/test_bootstrap.py index 8a97762..2d4e806 100644 --- a/integration-tests/test_bootstrap.py +++ b/integration-tests/test_bootstrap.py @@ -162,7 +162,7 @@ def verify_progress_page(expected_status_code, timeout): if b"HTTP/1.0 200 OK" in resp: progress_page_status = True break - except Exception as e: + except Exception: time.sleep(2) continue diff --git a/integration-tests/test_hub.py b/integration-tests/test_hub.py index 9991e0c..faba9ea 100644 --- a/integration-tests/test_hub.py +++ b/integration-tests/test_hub.py @@ -1,18 +1,20 @@ -import requests -from hubtraf.user import User -from hubtraf.auth.dummy import login_dummy -from jupyterhub.utils import exponential_backoff -import secrets -import pytest -from functools import partial import asyncio -import pwd import grp +import pwd +import secrets import subprocess +from functools import partial from os import system -from tljh.normalize import generate_system_username + +import pytest +import requests +from hubtraf.auth.dummy import login_dummy +from hubtraf.user import User +from jupyterhub.utils import exponential_backoff from packaging.version import Version as V +from tljh.normalize import generate_system_username + # Use sudo to invoke it, since this is how users invoke it. # This catches issues with PATH TLJH_CONFIG_PATH = ["sudo", "tljh-config"] diff --git a/integration-tests/test_install.py b/integration-tests/test_install.py index 1391ddd..06840f6 100644 --- a/integration-tests/test_install.py +++ b/integration-tests/test_install.py @@ -1,15 +1,14 @@ -from contextlib import contextmanager -from concurrent.futures import ProcessPoolExecutor -from functools import partial import grp import os import pwd import subprocess import sys +from concurrent.futures import ProcessPoolExecutor +from contextlib import contextmanager +from functools import partial import pytest - ADMIN_GROUP = "jupyterhub-admins" USER_GROUP = "jupyterhub-users" INSTALL_PREFIX = os.environ.get("TLJH_INSTALL_PREFIX", "/opt/tljh") @@ -93,7 +92,6 @@ def permissions_test(group, path, *, readable=None, writable=None, dirs_only=Fal # check if the path should be writable if writable is not None: if access(path, os.W_OK) != writable: - stat = os.stat(path) info = pool.submit(debug_uid_gid).result() failures.append( "{} {} should {}be writable by {} [{}]".format( @@ -104,7 +102,6 @@ def permissions_test(group, path, *, readable=None, writable=None, dirs_only=Fal # check if the path should be readable if readable is not None: if access(path, os.R_OK) != readable: - stat = os.stat(path) info = pool.submit(debug_uid_gid).result() failures.append( "{} {} should {}be readable by {} [{}]".format( diff --git a/integration-tests/test_proxy.py b/integration-tests/test_proxy.py index 2cb78f7..6aca51e 100644 --- a/integration-tests/test_proxy.py +++ b/integration-tests/test_proxy.py @@ -2,19 +2,19 @@ import os import shutil import ssl -from subprocess import check_call import time +from subprocess import check_call -import toml -from tornado.httpclient import HTTPClient, HTTPRequest, HTTPClientError import pytest +import toml +from tornado.httpclient import HTTPClient, HTTPClientError, HTTPRequest from tljh.config import ( + CONFIG_DIR, + CONFIG_FILE, + STATE_DIR, reload_component, set_config_value, - CONFIG_FILE, - CONFIG_DIR, - STATE_DIR, ) @@ -36,7 +36,6 @@ def send_request(url, max_sleep, validate_cert=True, username=None, password=Non break except Exception as e: print(e) - pass return resp @@ -134,7 +133,7 @@ def test_extra_traefik_config(): HTTPClient().fetch(req) success = True break - except Exception as e: + except Exception: pass assert success == True diff --git a/integration-tests/test_simplest_plugin.py b/integration-tests/test_simplest_plugin.py index eebff27..171578c 100644 --- a/integration-tests/test_simplest_plugin.py +++ b/integration-tests/test_simplest_plugin.py @@ -1,13 +1,14 @@ """ Test simplest plugin """ -from ruamel.yaml import YAML -import requests import os import subprocess -from tljh.config import CONFIG_FILE, USER_ENV_PREFIX, HUB_ENV_PREFIX +import requests +from ruamel.yaml import YAML + from tljh import user +from tljh.config import CONFIG_FILE, HUB_ENV_PREFIX, USER_ENV_PREFIX yaml = YAML(typ="rt") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..170be20 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +# autoflake is used for autoformatting Python code +# +# ref: https://github.com/PyCQA/autoflake#readme +# +[tool.autoflake] +ignore-init-module-imports = true +remove-all-unused-imports = true +remove-duplicate-keys = true +remove-unused-variables = true + + +# isort is used for autoformatting Python code +# +# ref: https://pycqa.github.io/isort/ +# +[tool.isort] +profile = "black" + + +# black is used for autoformatting Python code +# +# ref: https://black.readthedocs.io/en/stable/ +# +[tool.black] +# target-version should be all supported versions, see +# https://github.com/psf/black/issues/751#issuecomment-473066811 +target_version = [ + "py36", + "py37", + "py38", + "py39", + "py310", + "py311", +] diff --git a/setup.py b/setup.py index 7a179c6..12d7240 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup setup( name="the-littlest-jupyterhub", diff --git a/tests/conftest.py b/tests/conftest.py index e92884a..cf765b9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ """pytest fixtures""" -from importlib import reload import os import types +from importlib import reload from unittest import mock import pytest diff --git a/tests/test_bootstrap_functions.py b/tests/test_bootstrap_functions.py index c579ac8..799dc3c 100644 --- a/tests/test_bootstrap_functions.py +++ b/tests/test_bootstrap_functions.py @@ -5,9 +5,10 @@ import sys sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -from bootstrap import bootstrap import pytest +from bootstrap import bootstrap + @pytest.mark.parametrize( "requested,expected", diff --git a/tests/test_conda.py b/tests/test_conda.py index 46b7cac..6d44595 100644 --- a/tests/test_conda.py +++ b/tests/test_conda.py @@ -1,20 +1,20 @@ """ Test conda commandline wrappers """ -from tljh import conda -from tljh import installer import os -import pytest import subprocess import tempfile +import pytest + +from tljh import conda, installer + @pytest.fixture(scope="module") def prefix(): """ Provide a temporary directory with a mambaforge conda environment """ - machine = os.uname().machine installer_url, checksum = installer._mambaforge_url() with tempfile.TemporaryDirectory() as tmpdir: with conda.download_miniconda_installer( diff --git a/tests/test_config.py b/tests/test_config.py index 5d227da..c091060 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -56,7 +56,7 @@ def test_set_overwrite(): def test_unset_no_mutate(): conf = {"a": "b"} - new_conf = config.unset_item_from_config(conf, "a") + config.unset_item_from_config(conf, "a") assert conf == {"a": "b"} diff --git a/tests/test_configurer.py b/tests/test_configurer.py index bbef8c4..c348236 100644 --- a/tests/test_configurer.py +++ b/tests/test_configurer.py @@ -2,10 +2,11 @@ Test configurer """ -from traitlets.config import Config import os import sys +from traitlets.config import Config + from tljh import configurer diff --git a/tests/test_installer.py b/tests/test_installer.py index 7675bd0..1d0699c 100644 --- a/tests/test_installer.py +++ b/tests/test_installer.py @@ -3,15 +3,14 @@ Unit test functions in installer.py """ import json import os +from subprocess import PIPE, run from unittest import mock -from subprocess import run, PIPE -from packaging.version import parse as V -from packaging.specifiers import SpecifierSet import pytest +from packaging.specifiers import SpecifierSet +from packaging.version import parse as V -from tljh import conda -from tljh import installer +from tljh import conda, installer from tljh.yaml import yaml diff --git a/tests/test_migrator.py b/tests/test_migrator.py index 24e67a8..87275f6 100644 --- a/tests/test_migrator.py +++ b/tests/test_migrator.py @@ -4,7 +4,7 @@ Unit test functions in installer.py import os from datetime import date -from tljh import migrator, config +from tljh import config, migrator def test_migrate_config(tljh_dir): diff --git a/tests/test_traefik.py b/tests/test_traefik.py index ecda0ce..0cb406b 100644 --- a/tests/test_traefik.py +++ b/tests/test_traefik.py @@ -1,11 +1,10 @@ """Test traefik configuration""" import os -import toml import pytest +import toml -from tljh import config -from tljh import traefik +from tljh import config, traefik def test_download_traefik(tmpdir): diff --git a/tests/test_user.py b/tests/test_user.py index aa5accd..5bda68d 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,15 +1,17 @@ """ Test wrappers in tljw.user module """ -from tljh import user +import grp import os import os.path +import pwd import stat import uuid -import pwd -import grp + import pytest +from tljh import user + def test_ensure_user(): """ diff --git a/tests/test_utils.py b/tests/test_utils.py index 449ecbe..e54047a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,9 @@ -import pytest -from tljh import utils -import subprocess import logging +import subprocess + +import pytest + +from tljh import utils def test_run_subprocess_exception(mocker): diff --git a/tljh/apt.py b/tljh/apt.py index bf20e67..b5b0845 100644 --- a/tljh/apt.py +++ b/tljh/apt.py @@ -3,6 +3,7 @@ Utilities for working with the apt package manager """ import os import subprocess + from tljh import utils diff --git a/tljh/conda.py b/tljh/conda.py index 206e4df..df9a027 100644 --- a/tljh/conda.py +++ b/tljh/conda.py @@ -1,12 +1,12 @@ """ Wrap conda commandline program """ +import contextlib +import hashlib +import json +import logging import os import subprocess -import json -import hashlib -import contextlib -import logging import tempfile import time diff --git a/tljh/config.py b/tljh/config.py index e705c10..4459621 100644 --- a/tljh/config.py +++ b/tljh/config.py @@ -13,18 +13,17 @@ tljh-config show firstlevel.second_level """ import argparse -from collections.abc import Sequence, Mapping -from copy import deepcopy import os import re import sys import time +from collections.abc import Mapping, Sequence +from copy import deepcopy import requests from .yaml import yaml - INSTALL_PREFIX = os.environ.get("TLJH_INSTALL_PREFIX", "/opt/tljh") HUB_ENV_PREFIX = os.path.join(INSTALL_PREFIX, "hub") USER_ENV_PREFIX = os.path.join(INSTALL_PREFIX, "user") diff --git a/tljh/hooks.py b/tljh/hooks.py index ddb1f3f..ddaa2a3 100644 --- a/tljh/hooks.py +++ b/tljh/hooks.py @@ -12,7 +12,6 @@ def tljh_extra_user_conda_packages(): """ Return list of extra conda packages to install in user environment. """ - pass @hookspec @@ -20,7 +19,6 @@ def tljh_extra_user_pip_packages(): """ Return list of extra pip packages to install in user environment. """ - pass @hookspec @@ -28,7 +26,6 @@ def tljh_extra_hub_pip_packages(): """ Return list of extra pip packages to install in the hub environment. """ - pass @hookspec @@ -38,7 +35,6 @@ def tljh_extra_apt_packages(): These will be installed before additional pip or conda packages. """ - pass @hookspec @@ -49,7 +45,6 @@ def tljh_custom_jupyterhub_config(c): Anything you can put in `jupyterhub_config.py` can be here. """ - pass @hookspec @@ -62,7 +57,6 @@ def tljh_config_post_install(config): be the serialized contents of config, so try to not overwrite anything the user might have explicitly set. """ - pass @hookspec @@ -73,7 +67,6 @@ def tljh_post_install(): This can be arbitrary Python code. """ - pass @hookspec @@ -82,4 +75,3 @@ def tljh_new_user_create(username): Script to be executed after a new user has been added. This can be arbitrary Python code. """ - pass diff --git a/tljh/installer.py b/tljh/installer.py index 4ca2470..755326e 100644 --- a/tljh/installer.py +++ b/tljh/installer.py @@ -17,15 +17,7 @@ import pluggy import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning -from tljh import ( - apt, - conda, - hooks, - migrator, - systemd, - traefik, - user, -) +from tljh import apt, conda, hooks, migrator, systemd, traefik, user from .config import ( CONFIG_DIR, @@ -518,7 +510,6 @@ def main(): print("Progress page server stopped successfully.") except Exception as e: logger.error(f"Couldn't stop the progress page server. Exception was {e}.") - pass ensure_jupyterhub_service(HUB_ENV_PREFIX) ensure_jupyterhub_running() diff --git a/tljh/jupyterhub_config.py b/tljh/jupyterhub_config.py index a553f2c..c61c06c 100644 --- a/tljh/jupyterhub_config.py +++ b/tljh/jupyterhub_config.py @@ -2,14 +2,15 @@ JupyterHub config for the littlest jupyterhub. """ -from glob import glob import os +from glob import glob + +from jupyterhub_traefik_proxy import TraefikTomlProxy from tljh import configurer -from tljh.config import INSTALL_PREFIX, USER_ENV_PREFIX, CONFIG_DIR -from tljh.utils import get_plugin_manager +from tljh.config import CONFIG_DIR, INSTALL_PREFIX, USER_ENV_PREFIX from tljh.user_creating_spawner import UserCreatingSpawner -from jupyterhub_traefik_proxy import TraefikTomlProxy +from tljh.utils import get_plugin_manager c.JupyterHub.spawner_class = UserCreatingSpawner diff --git a/tljh/log.py b/tljh/log.py index e615f7b..ed7eca4 100644 --- a/tljh/log.py +++ b/tljh/log.py @@ -1,6 +1,6 @@ """Setup tljh logging""" -import os import logging +import os from .config import INSTALL_PREFIX diff --git a/tljh/migrator.py b/tljh/migrator.py index 75336ad..9f00d83 100644 --- a/tljh/migrator.py +++ b/tljh/migrator.py @@ -1,16 +1,11 @@ """Migration utilities for upgrading tljh""" -import os -from datetime import date import logging +import os import shutil +from datetime import date -from tljh.config import ( - CONFIG_DIR, - CONFIG_FILE, - INSTALL_PREFIX, -) - +from tljh.config import CONFIG_DIR, CONFIG_FILE, INSTALL_PREFIX logger = logging.getLogger("tljh") diff --git a/tljh/systemd.py b/tljh/systemd.py index d4fcf6c..f274fab 100644 --- a/tljh/systemd.py +++ b/tljh/systemd.py @@ -3,8 +3,8 @@ Wraps systemctl to install, uninstall, start & stop systemd services. If we use a debian package instead, we can get rid of all this code. """ -import subprocess import os +import subprocess def reload_daemon(): diff --git a/tljh/traefik.py b/tljh/traefik.py index e815efb..c93a9fa 100644 --- a/tljh/traefik.py +++ b/tljh/traefik.py @@ -3,14 +3,15 @@ import hashlib import os from glob import glob -from jinja2 import Template -from passlib.apache import HtpasswdFile import backoff import requests import toml +from jinja2 import Template +from passlib.apache import HtpasswdFile + +from tljh.configurer import _merge_dictionaries, load_config from .config import CONFIG_DIR -from tljh.configurer import load_config, _merge_dictionaries # traefik 2.7.x is not supported yet, use v1.7.x for now # see: https://github.com/jupyterhub/traefik-proxy/issues/97 diff --git a/tljh/user_creating_spawner.py b/tljh/user_creating_spawner.py index c6f07f3..aa455e3 100644 --- a/tljh/user_creating_spawner.py +++ b/tljh/user_creating_spawner.py @@ -1,9 +1,9 @@ -from tljh.normalize import generate_system_username -from tljh import user -from tljh import configurer -from systemdspawner import SystemdSpawner -from traitlets import Dict, Unicode, List from jupyterhub_configurator.mixins import ConfiguratorSpawnerMixin +from systemdspawner import SystemdSpawner +from traitlets import Dict, List, Unicode + +from tljh import configurer, user +from tljh.normalize import generate_system_username class CustomSpawner(SystemdSpawner): diff --git a/tljh/yaml.py b/tljh/yaml.py index 3ff8a8d..e51381e 100644 --- a/tljh/yaml.py +++ b/tljh/yaml.py @@ -3,8 +3,8 @@ ensures the same yaml settings for reading/writing throughout tljh """ -from ruamel.yaml.composer import Composer from ruamel.yaml import YAML +from ruamel.yaml.composer import Composer class _NoEmptyFlowComposer(Composer):