diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..858d673868 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +# Initial pre-commit reformat diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 0000000000..0559a09e0c --- /dev/null +++ b/.gitconfig @@ -0,0 +1,2 @@ +[blame] + ignoreRevsFile = .git-blame-ignore-revs diff --git a/.github/workflows/python-linux.yml b/.github/workflows/python-linux.yml index b57ecf0463..81bc8d7bff 100644 --- a/.github/workflows/python-linux.yml +++ b/.github/workflows/python-linux.yml @@ -5,6 +5,30 @@ on: pull_request: branches: '*' jobs: + # Run "pre-commit run --all-files" + pre-commit: + runs-on: ubuntu-20.04 + timeout-minutes: 2 + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + + # ref: https://github.com/pre-commit/action + - uses: pre-commit/action@v2.0.0 + - name: Help message if pre-commit fail + if: ${{ failure() }} + run: | + echo "You can install pre-commit hooks to automatically run formatting" + echo "on each commit with:" + echo " pre-commit install" + echo "or you can run by hand on staged files with" + echo " pre-commit run" + echo "or after-the-fact on already committed files with" + echo " pre-commit run --all-files" + build: runs-on: ${{ matrix.os }}-latest strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..32bd0e3f42 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +repos: + - repo: https://github.com/asottile/reorder_python_imports + rev: v1.9.0 + hooks: + - id: reorder-python-imports + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + args: ["--line-length", "100"] + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.2.1 + hooks: + - id: prettier + - repo: https://gitlab.com/pycqa/flake8 + rev: "3.8.4" + hooks: + - id: flake8 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.4.0 + hooks: + - id: end-of-file-fixer + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: requirements-txt-fixer + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v7.32.0 + hooks: + - id: eslint + - repo: https://github.com/pre-commit/mirrors-pylint + rev: v3.0.0a3 + hooks: + - id: pylint + args: [--disable=all, --enable=unused-import] diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..b0e34edeb4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +**/templates/*.html diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c58a0d446d..b25be508d9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -37,6 +37,33 @@ from any directory in your system with:: jupyter server + +Code Styling +----------------------------- +`jupyter_server` has adopted automatic code formatting so you shouldn't +need to worry too much about your code style. +As long as your code is valid, +the pre-commit hook should take care of how it should look. +To install `pre-commit`, run the following:: + + pip install pre-commit + pre-commit install + + +You can invoke the pre-commit hook by hand at any time with:: + + pre-commit run + +which should run any autoformatting on your code +and tell you about any errors it couldn't fix automatically. +You may also install [black integration](https://github.com/psf/black#editor-integration) +into your text editor to format code automatically. + +If you have already committed files before setting up the pre-commit +hook with ``pre-commit install``, you can fix everything up using +``pre-commit run --all-files``. You need to make the fixing commit +yourself after that. + Troubleshooting the Installation -------------------------------- diff --git a/docs/source/conf.py b/docs/source/conf.py index 79f9df3ed6..b9be17d3eb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,7 +16,6 @@ import sys import os import os.path as osp -import shlex import shutil HERE = osp.abspath(osp.dirname(__file__)) @@ -378,7 +377,7 @@ spelling_word_list_filename='spelling_wordlist.txt' # import before any doc is built, so _ is guaranteed to be injected -import jupyter_server.transutils +import jupyter_server.transutils # pylint: disable=unused-import def setup(app): diff --git a/examples/simple/setup.py b/examples/simple/setup.py old mode 100755 new mode 100644 diff --git a/examples/simple/simple_ext1/application.py b/examples/simple/simple_ext1/application.py index 4ddf889096..3cf9211ad7 100644 --- a/examples/simple/simple_ext1/application.py +++ b/examples/simple/simple_ext1/application.py @@ -1,4 +1,4 @@ -import os, jinja2 +import os from traitlets import Unicode from jupyter_server.extension.application import ExtensionApp, ExtensionAppJinjaMixin from .handlers import (DefaultHandler, RedirectHandler, diff --git a/examples/simple/simple_ext1/handlers.py b/examples/simple/simple_ext1/handlers.py index 77b2903b64..c3765edeca 100644 --- a/examples/simple/simple_ext1/handlers.py +++ b/examples/simple/simple_ext1/handlers.py @@ -7,7 +7,7 @@ def get(self): # The name of the extension to which this handler is linked. self.log.info("Extension Name in {} Default Handler: {}".format(self.name, self.name)) # A method for getting the url to static files (prefixed with /static/). - self.log.info("Static URL for / in simple_ext1 Default Handler:".format(self.static_url(path='/'))) + self.log.info("Static URL for / in simple_ext1 Default Handler: {}".format(self.static_url(path='/'))) self.write('

Hello Simple 1 - I am the default...

') self.write('Config in {} Default Handler: {}'.format(self.name, self.config)) diff --git a/examples/simple/simple_ext2/application.py b/examples/simple/simple_ext2/application.py index be59ce9bfa..d485265691 100644 --- a/examples/simple/simple_ext2/application.py +++ b/examples/simple/simple_ext2/application.py @@ -1,4 +1,4 @@ -import os, jinja2 +import os from traitlets import Unicode from jupyter_server.extension.application import ExtensionApp, ExtensionAppJinjaMixin from .handlers import ParameterHandler, TemplateHandler, IndexHandler, ErrorHandler diff --git a/jupyter_server/_sysinfo.py b/jupyter_server/_sysinfo.py index ae0999fe9e..6e57102567 100644 --- a/jupyter_server/_sysinfo.py +++ b/jupyter_server/_sysinfo.py @@ -8,11 +8,10 @@ import os import platform -import pprint import sys import subprocess -from ipython_genutils import py3compat, encoding +from ipython_genutils import encoding import jupyter_server diff --git a/jupyter_server/base/handlers.py b/jupyter_server/base/handlers.py old mode 100755 new mode 100644 index 3d1a336301..6877ea7284 --- a/jupyter_server/base/handlers.py +++ b/jupyter_server/base/handlers.py @@ -10,7 +10,6 @@ import mimetypes import os import re -import sys import traceback import types import warnings @@ -18,7 +17,7 @@ from http.cookies import Morsel from urllib.parse import urlparse from jinja2 import TemplateNotFound -from tornado import web, gen, escape, httputil +from tornado import web, escape, httputil from tornado.log import app_log import prometheus_client diff --git a/jupyter_server/extension/manager.py b/jupyter_server/extension/manager.py index 83f1af7943..f9dcc2e8b6 100644 --- a/jupyter_server/extension/manager.py +++ b/jupyter_server/extension/manager.py @@ -15,7 +15,7 @@ Instance, default, observe, - validate, + validate as validate_trait, ) from .config import ExtensionConfigManager @@ -36,7 +36,7 @@ class ExtensionPoint(HasTraits): metadata = Dict() - @validate('metadata') + @validate_trait('metadata') def _valid_metadata(self, proposed): metadata = proposed['value'] # Verify that the metadata has a "name" key. @@ -180,7 +180,7 @@ def __init__(self, *args, **kwargs): _linked_points = {} - @validate("name") + @validate_trait("name") def _validate_name(self, proposed): name = proposed['value'] self._extension_points = {} diff --git a/jupyter_server/nbconvert/handlers.py b/jupyter_server/nbconvert/handlers.py index b9cb711a68..85a48f9af6 100644 --- a/jupyter_server/nbconvert/handlers.py +++ b/jupyter_server/nbconvert/handlers.py @@ -7,7 +7,7 @@ import os import zipfile -from tornado import web, escape +from tornado import web from tornado.log import app_log from ..base.handlers import ( diff --git a/jupyter_server/prometheus/metrics.py b/jupyter_server/prometheus/metrics.py index 71192b4eaf..3ebe4d074d 100644 --- a/jupyter_server/prometheus/metrics.py +++ b/jupyter_server/prometheus/metrics.py @@ -9,7 +9,8 @@ # Jupyter Notebook also defines these metrics. Re-defining them results in a ValueError. # Try to de-duplicate by using the ones in Notebook if available. # See https://github.com/jupyter/jupyter_server/issues/209 - from notebook.prometheus.metrics import HTTP_REQUEST_DURATION_SECONDS, TERMINAL_CURRENTLY_RUNNING_TOTAL, KERNEL_CURRENTLY_RUNNING_TOTAL + # pylint: disable=unused-import + from notebook.prometheus.metrics import HTTP_REQUEST_DURATION_SECONDS,TERMINAL_CURRENTLY_RUNNING_TOTAL, KERNEL_CURRENTLY_RUNNING_TOTAL except ImportError: diff --git a/jupyter_server/serverapp.py b/jupyter_server/serverapp.py old mode 100755 new mode 100644 index 129e83baed..33fc4256ed --- a/jupyter_server/serverapp.py +++ b/jupyter_server/serverapp.py @@ -23,10 +23,8 @@ import socket import stat import sys -import tempfile import threading import time -import warnings import webbrowser import urllib import inspect @@ -43,7 +41,7 @@ from jupyter_core.paths import secure_write from jupyter_server.transutils import trans, _i18n -from jupyter_server.utils import run_sync_in_loop +from jupyter_server.utils import run_sync_in_loop, urljoin, pathname2url # the minimum viable tornado version: needs to be kept in sync with setup.py MIN_TORNADO = (6, 1, 0) @@ -93,22 +91,21 @@ ) from jupyter_core.paths import jupyter_config_path from jupyter_client import KernelManager -from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel, NATIVE_KERNEL_NAME +from jupyter_client.kernelspec import KernelSpecManager from jupyter_client.session import Session from nbformat.sign import NotebookNotary from traitlets import ( Any, Dict, Unicode, Integer, List, Bool, Bytes, Instance, TraitError, Type, Float, observe, default, validate ) -from jupyter_core.paths import jupyter_runtime_dir, jupyter_path +from jupyter_core.paths import jupyter_runtime_dir from jupyter_server._sysinfo import get_sys_info -from jupyter_server._tz import utcnow, utcfromtimestamp +from jupyter_server._tz import utcnow from jupyter_server.utils import ( url_path_join, check_pid, url_escape, - urljoin, pathname2url, unix_socket_in_use, urlencode_unix_socket_path, diff --git a/jupyter_server/services/config/handlers.py b/jupyter_server/services/config/handlers.py index aae6480757..245b344bf3 100644 --- a/jupyter_server/services/config/handlers.py +++ b/jupyter_server/services/config/handlers.py @@ -3,9 +3,6 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import json -import os -import io -import errno from tornado import web from ...base.handlers import APIHandler diff --git a/jupyter_server/services/contents/filemanager.py b/jupyter_server/services/contents/filemanager.py index 1ceaaefb62..ee92155931 100644 --- a/jupyter_server/services/contents/filemanager.py +++ b/jupyter_server/services/contents/filemanager.py @@ -21,7 +21,7 @@ from .manager import AsyncContentsManager, ContentsManager from ipython_genutils.importstring import import_item -from traitlets import Any, Unicode, Bool, TraitError, observe, default, validate +from traitlets import Any, Unicode, Bool, TraitError, default, validate from jupyter_core.paths import exists, is_hidden, is_file_hidden from jupyter_server import _tz as tz diff --git a/jupyter_server/services/contents/manager.py b/jupyter_server/services/contents/manager.py index 029966fe4b..87a13330e3 100644 --- a/jupyter_server/services/contents/manager.py +++ b/jupyter_server/services/contents/manager.py @@ -6,7 +6,6 @@ from fnmatch import fnmatch import itertools import json -import os import re from tornado.web import HTTPError, RequestHandler diff --git a/jupyter_server/tests/auth/test_security.py b/jupyter_server/tests/auth/test_security.py index ff4ea669ec..ef7943401e 100644 --- a/jupyter_server/tests/auth/test_security.py +++ b/jupyter_server/tests/auth/test_security.py @@ -1,4 +1,3 @@ -import pytest from jupyter_server.auth.security import passwd, passwd_check diff --git a/jupyter_server/tests/extension/test_entrypoint.py b/jupyter_server/tests/extension/test_entrypoint.py index 9a36e5117e..97af9ad17b 100644 --- a/jupyter_server/tests/extension/test_entrypoint.py +++ b/jupyter_server/tests/extension/test_entrypoint.py @@ -1,4 +1,3 @@ -import os import pytest @@ -13,4 +12,4 @@ def test_server_extension_list(jp_environ, script_runner): 'extension', 'list', ) - assert ret.success \ No newline at end of file + assert ret.success diff --git a/jupyter_server/tests/services/contents/test_api.py b/jupyter_server/tests/services/contents/test_api.py index ee428c0fb9..4d91c74422 100644 --- a/jupyter_server/tests/services/contents/test_api.py +++ b/jupyter_server/tests/services/contents/test_api.py @@ -2,7 +2,6 @@ import json import pathlib import pytest -from urllib.parse import ParseResult, urlunparse import tornado diff --git a/jupyter_server/tests/services/kernels/test_api.py b/jupyter_server/tests/services/kernels/test_api.py index ddd91c8513..bfad2cef65 100644 --- a/jupyter_server/tests/services/kernels/test_api.py +++ b/jupyter_server/tests/services/kernels/test_api.py @@ -1,16 +1,10 @@ -import sys import time import json import pytest - - import tornado -import urllib.parse -from tornado.escape import url_escape from jupyter_client.kernelspec import NATIVE_KERNEL_NAME -from jupyter_client.multikernelmanager import AsyncMultiKernelManager from jupyter_server.utils import url_path_join from ...utils import expected_http_error diff --git a/jupyter_server/tests/services/kernels/test_config.py b/jupyter_server/tests/services/kernels/test_config.py index befdd1cb19..f2761fb68c 100644 --- a/jupyter_server/tests/services/kernels/test_config.py +++ b/jupyter_server/tests/services/kernels/test_config.py @@ -1,4 +1,3 @@ -import sys import pytest from traitlets.config import Config from jupyter_server.services.kernels.kernelmanager import AsyncMappingKernelManager diff --git a/jupyter_server/tests/services/kernels/test_cull.py b/jupyter_server/tests/services/kernels/test_cull.py index cc58818b3b..158745cf83 100644 --- a/jupyter_server/tests/services/kernels/test_cull.py +++ b/jupyter_server/tests/services/kernels/test_cull.py @@ -1,8 +1,6 @@ import asyncio import json import platform -import sys -import time import pytest from traitlets.config import Config from tornado.httpclient import HTTPClientError diff --git a/jupyter_server/tests/test_gateway.py b/jupyter_server/tests/test_gateway.py index 6c18bba28f..86ab2908db 100644 --- a/jupyter_server/tests/test_gateway.py +++ b/jupyter_server/tests/test_gateway.py @@ -7,7 +7,6 @@ from datetime import datetime from tornado.web import HTTPError from tornado.httpclient import HTTPRequest, HTTPResponse -from jupyter_server.serverapp import ServerApp from jupyter_server.gateway.managers import GatewayClient from jupyter_server.utils import ensure_async diff --git a/jupyter_server/tests/test_serverapp.py b/jupyter_server/tests/test_serverapp.py index a850abd067..9d4001ca4a 100644 --- a/jupyter_server/tests/test_serverapp.py +++ b/jupyter_server/tests/test_serverapp.py @@ -12,8 +12,7 @@ from jupyter_server.serverapp import ( ServerApp, list_running_servers, - JupyterPasswordApp, - JupyterServerStopApp + JupyterPasswordApp ) from jupyter_server.auth.security import passwd_check diff --git a/jupyter_server/tests/test_terminal.py b/jupyter_server/tests/test_terminal.py index 7ad638d582..f11185510a 100644 --- a/jupyter_server/tests/test_terminal.py +++ b/jupyter_server/tests/test_terminal.py @@ -3,7 +3,6 @@ import pytest import json import asyncio -import sys from tornado.httpclient import HTTPClientError from traitlets.config import Config diff --git a/jupyter_server/tests/unix_sockets/test_api.py b/jupyter_server/tests/unix_sockets/test_api.py index aaa0d219e7..3d5a69ccb2 100644 --- a/jupyter_server/tests/unix_sockets/test_api.py +++ b/jupyter_server/tests/unix_sockets/test_api.py @@ -7,17 +7,12 @@ reason="Unix sockets are not available on Windows." ) -import os import urllib -import pathlib if not sys.platform.startswith('win'): from tornado.netutil import bind_unix_socket -from tornado.escape import url_escape - import jupyter_server.serverapp -from jupyter_server import DEFAULT_JUPYTER_SERVER_PORT from jupyter_server.utils import ( url_path_join, urlencode_unix_socket, diff --git a/jupyter_server/tests/unix_sockets/test_serverapp_integration.py b/jupyter_server/tests/unix_sockets/test_serverapp_integration.py index 6d13524a14..263de59754 100644 --- a/jupyter_server/tests/unix_sockets/test_serverapp_integration.py +++ b/jupyter_server/tests/unix_sockets/test_serverapp_integration.py @@ -10,7 +10,6 @@ import os import subprocess -import pathlib import time from jupyter_server.utils import urlencode_unix_socket, urlencode_unix_socket_path @@ -164,7 +163,7 @@ def test_launch_socket_collision(jp_unix_socket_file): except Exception as ex: raise AssertionError(f"expected 'already in use' error, got '{ex}'!") else: - raise AssertionError(f"expected 'already in use' error, got success instead!") + raise AssertionError("expected 'already in use' error, got success instead!") # Stop the background server, ensure it's stopped and wait on the process to exit. subprocess.check_call(['jupyter-server', 'stop', sock]) diff --git a/jupyter_server/traittypes.py b/jupyter_server/traittypes.py index 9f31232197..40c67be2e1 100644 --- a/jupyter_server/traittypes.py +++ b/jupyter_server/traittypes.py @@ -1,5 +1,6 @@ +from ast import literal_eval import inspect -from traitlets import ClassBasedTraitType, Undefined, warn +from traitlets import ClassBasedTraitType, Undefined, TraitError # Traitlet's 5.x includes a set of utilities for building # description strings for objects. Traitlets 5.x does not @@ -10,7 +11,6 @@ try: from traitlets.utils.descriptions import describe except ImportError: - import inspect import re import types @@ -139,6 +139,17 @@ def add_article(name, definite=False, capital=False): else: return result + def _prefix(value): + if isinstance(value, types.MethodType): + name = describe(None, value.__self__, verbose=True) + '.' + else: + module = inspect.getmodule(value) + if module is not None and module.__name__ != "builtins": + name = module.__name__ + '.' + else: + name = "" + return name + class TypeFromClasses(ClassBasedTraitType): """A trait whose value must be a subclass of a class in a specified list of classes.""" @@ -287,7 +298,7 @@ class or its subclasses. Our implementation is quite different self.klasses = klasses else: raise TraitError('The klasses attribute must be a list of class names or classes' - ' not: %r' % klass) + ' not: %r' % klasses) if (kw is not None) and not isinstance(kw, dict): raise TraitError("The 'kw' argument must be a dict or None.") @@ -350,4 +361,4 @@ def default_value_repr(self): return repr(self.make_dynamic_default()) def from_string(self, s): - return _safe_literal_eval(s) + return literal_eval(s) diff --git a/jupyter_server/utils.py b/jupyter_server/utils.py index 7af3c3a524..9a062dfc03 100644 --- a/jupyter_server/utils.py +++ b/jupyter_server/utils.py @@ -14,13 +14,12 @@ from distutils.version import LooseVersion from contextlib import contextmanager -from urllib.parse import (quote, unquote, urlparse, urljoin, +from urllib.parse import (quote, unquote, urlparse, urlsplit, urlunsplit, SplitResult) -from urllib.request import pathname2url - +from urllib.parse import urljoin # pylint: disable=unused-import +from urllib.request import pathname2url # pylint: disable=unused-import from tornado.httpclient import AsyncHTTPClient, HTTPClient, HTTPRequest from tornado.netutil import Resolver -from tornado.ioloop import IOLoop def url_path_join(*pieces): diff --git a/setup.cfg b/setup.cfg index 07572b147a..1b27f53ff6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,3 +63,12 @@ console_scripts = [options.packages.find] exclude = ['docs*', 'examples*'] + +[flake8] +ignore = E, C, W, F401, F403, F811, F841, E402, I100, I101, D400 +builtins = c, get_config +exclude = + .cache, + .github, + docs, + setup.py