diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5a333a704..4ef978f49 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,6 +57,18 @@ jobs: pip install codecov coverage[toml] codecov + test_lint: + name: Test Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 + - name: Run Linters + run: | + hatch run lint:style + pipx run 'validate-pyproject[all]' pyproject.toml + pipx run doc8 --max-line-length=200 --ignore-path=docs/source/other/full-config.rst + check_release: runs-on: ubuntu-latest steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21822ba7b..3c8b7ff96 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +ci: + autoupdate_schedule: monthly + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 @@ -15,54 +18,25 @@ repos: - id: check-builtin-literals - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black - args: ["--line-length", "100"] - - - repo: https://github.com/PyCQA/isort - rev: 5.10.1 - hooks: - - id: isort - files: \.py$ - args: [--profile=black] - stages: [manual] - - - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.10.1 + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.19.2 hooks: - - id: validate-pyproject - stages: [manual] + - id: check-github-workflows - repo: https://github.com/executablebooks/mdformat rev: 0.7.16 hooks: - id: mdformat + additional_dependencies: + [mdformat-gfm, mdformat-frontmatter, mdformat-footnote] - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 - hooks: - - id: pyupgrade - args: [--py37-plus] - - - repo: https://github.com/PyCQA/doc8 - rev: v1.0.0 - hooks: - - id: doc8 - args: [--max-line-length=200] - stages: [manual] - - - repo: https://github.com/john-hen/Flake8-pyproject - rev: 1.2.2 + - repo: https://github.com/psf/black + rev: 22.10.0 hooks: - - id: Flake8-pyproject - alias: flake8 - additional_dependencies: - ["flake8-bugbear==22.6.22", "flake8-implicit-str-concat==0.2.0"] - stages: [manual] + - id: black - - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.19.2 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.181 hooks: - - id: check-github-workflows + - id: ruff + args: ["--fix"] diff --git a/check_requirements.py b/check_requirements.py index a3c53fc74..ae10f441f 100644 --- a/check_requirements.py +++ b/check_requirements.py @@ -21,11 +21,11 @@ if errors: print('Missing deps in "all" reqs:') - print([e for e in errors]) + print(list(errors)) if remaining_all: print('Reqs in "all" but nowhere else:') - print([r for r in remaining_all]) + print(list(remaining_all)) if errors or remaining_all: sys.exit(1) diff --git a/docs/source/conf.py b/docs/source/conf.py index 0abace7ec..9eb629219 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,7 +24,7 @@ # Automatically generate config_options.rst with open(os.path.join(HERE, "..", "autogen_config.py")) as f: - exec(compile(f.read(), "autogen_config.py", "exec"), {}) + exec(compile(f.read(), "autogen_config.py", "exec"), {}) # noqa print("Created docs for config options") # -- General configuration ------------------------------------------------ @@ -82,7 +82,7 @@ # Get information from _version.py and use it to generate version and release _version_py = os.path.join(HERE, "../../nbconvert/_version.py") version_ns = {} -exec(compile(open(_version_py).read(), _version_py, "exec"), version_ns) +exec(compile(open(_version_py).read(), _version_py, "exec"), version_ns) # noqa # The short X.Y version. version = "%i.%i" % version_ns["version_info"][:2] # The full version, including alpha/beta/rc tags. diff --git a/docs/source/nbconvert_library.ipynb b/docs/source/nbconvert_library.ipynb index cd69c50d1..cb3f8e37b 100644 --- a/docs/source/nbconvert_library.ipynb +++ b/docs/source/nbconvert_library.ipynb @@ -71,6 +71,7 @@ "outputs": [], "source": [ "import nbformat\n", + "\n", "jake_notebook = nbformat.reads(response, as_version=4)\n", "jake_notebook.cells[0]" ] @@ -101,7 +102,7 @@ "\n", "# 2. Instantiate the exporter. We use the `classic` template for now; we'll get into more details\n", "# later about how to customize the exporter further.\n", - "html_exporter = HTMLExporter(template_name = 'classic')\n", + "html_exporter = HTMLExporter(template_name='classic')\n", "\n", "# 3. Process the notebook we loaded earlier\n", "(body, resources) = html_exporter.from_notebook_node(jake_notebook)" @@ -185,6 +186,7 @@ "source": [ "# Import the RST exproter\n", "from nbconvert import RSTExporter\n", + "\n", "# Instantiate it\n", "rst_exporter = RSTExporter()\n", "# Convert the notebook to RST format\n", @@ -237,6 +239,7 @@ "outputs": [], "source": [ "from IPython.display import Image\n", + "\n", "Image(data=resources['outputs']['output_3_0.png'], format='png')" ] }, @@ -297,6 +300,7 @@ "source": [ "# create a configuration object that changes the preprocessors\n", "from traitlets.config import Config\n", + "\n", "c = Config()\n", "c.HTMLExporter.preprocessors = ['nbconvert.preprocessors.ExtractOutputPreprocessor']\n", "\n", @@ -322,7 +326,7 @@ }, "outputs": [], "source": [ - "(_, resources) = html_exporter.from_notebook_node(jake_notebook)\n", + "(_, resources) = html_exporter.from_notebook_node(jake_notebook)\n", "(_, resources_with_fig) = html_exporter_with_figs.from_notebook_node(jake_notebook)\n", "\n", "print(\"resources without figures:\")\n", @@ -380,17 +384,18 @@ "from traitlets import Integer\n", "from nbconvert.preprocessors import Preprocessor\n", "\n", + "\n", "class PelicanSubCell(Preprocessor):\n", " \"\"\"A Pelican specific preprocessor to remove some of the cells of a notebook\"\"\"\n", - " \n", + "\n", " # I could also read the cells from nb.metadata.pelican if someone wrote a JS extension,\n", - " # but for now I'll stay with configurable value. \n", - " start = Integer(0, help=\"first cell of notebook to be converted\").tag(config=True)\n", - " end = Integer(-1, help=\"last cell of notebook to be converted\").tag(config=True)\n", - " \n", + " # but for now I'll stay with configurable value.\n", + " start = Integer(0, help=\"first cell of notebook to be converted\").tag(config=True)\n", + " end = Integer(-1, help=\"last cell of notebook to be converted\").tag(config=True)\n", + "\n", " def preprocess(self, nb, resources):\n", " self.log.info(\"I'll keep only cells from %d to %d\", self.start, self.end)\n", - " nb.cells = nb.cells[self.start:self.end] \n", + " nb.cells = nb.cells[self.start : self.end]\n", " return nb, resources" ] }, @@ -412,7 +417,7 @@ "outputs": [], "source": [ "# Create a new config object that configures both the new preprocessor, as well as the exporter\n", - "c = Config()\n", + "c = Config()\n", "c.PelicanSubCell.start = 4\n", "c.PelicanSubCell.end = 6\n", "c.RSTExporter.preprocessors = [PelicanSubCell]\n", @@ -443,14 +448,17 @@ "source": [ "from jinja2 import DictLoader\n", "\n", - "dl = DictLoader({'footer': \n", - "\"\"\"\n", + "dl = DictLoader(\n", + " {\n", + " 'footer': \"\"\"\n", "{%- extends 'lab/index.html.j2' -%} \n", "\n", "{% block footer %}\n", "FOOOOOOOOTEEEEER\n", "{% endblock footer %}\n", - "\"\"\"})\n", + "\"\"\"\n", + " }\n", + ")\n", "\n", "\n", "exportHTML = HTMLExporter(extra_loaders=[dl], template_file='footer')\n", diff --git a/nbconvert/exporters/pdf.py b/nbconvert/exporters/pdf.py index 1c72f3e9a..3befa0e72 100644 --- a/nbconvert/exporters/pdf.py +++ b/nbconvert/exporters/pdf.py @@ -15,7 +15,7 @@ from .latex import LatexExporter -class LatexFailed(IOError): +class LatexFailed(IOError): # noqa """Exception for failed latex run Captured latex output is in error.output. diff --git a/nbconvert/exporters/templateexporter.py b/nbconvert/exporters/templateexporter.py index a4aeb5889..8e7b6f9d7 100644 --- a/nbconvert/exporters/templateexporter.py +++ b/nbconvert/exporters/templateexporter.py @@ -122,7 +122,7 @@ def get_source(self, environment, template): return self.loader.get_source(environment, template) except TemplateNotFound: if template.endswith(self.extension): - raise TemplateNotFound(template) + raise TemplateNotFound(template) from None return self.loader.get_source(environment, template + self.extension) def list_templates(self): @@ -569,9 +569,9 @@ def _template_paths(self, prune=True, root_dirs=None): def get_compatibility_base_template_conf(cls, name): # Hard-coded base template confs to use for backwards compatibility for 5.x-only templates if name == "display_priority": - return dict(base_template="base") + return {"base_template": "base"} if name == "full": - return dict(base_template="classic", mimetypes={"text/html": True}) + return {"base_template": "classic", "mimetypes": {"text/html": True}} def get_template_names(self): # finds a list of template names where each successive template name is the base template diff --git a/nbconvert/exporters/tests/files/notebook2.ipynb b/nbconvert/exporters/tests/files/notebook2.ipynb index 1ff2e7532..f459aa2fe 100644 --- a/nbconvert/exporters/tests/files/notebook2.ipynb +++ b/nbconvert/exporters/tests/files/notebook2.ipynb @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "a = np.random.uniform(size=(100,100))" + "a = np.random.uniform(size=(100, 100))" ] }, { diff --git a/nbconvert/exporters/tests/files/notebook_inject.ipynb b/nbconvert/exporters/tests/files/notebook_inject.ipynb index 2ddb76129..f7f7173e4 100644 --- a/nbconvert/exporters/tests/files/notebook_inject.ipynb +++ b/nbconvert/exporters/tests/files/notebook_inject.ipynb @@ -5,257 +5,304 @@ "execution_count": null, "id": "79aede83-fba6-4715-bce6-9f3926b128a2", "metadata": { - "tags": ["FOO\">
alert('raw cell')" - ] + "cell_type": "raw", + "id": "372c2bf1", + "metadata": {}, + "source": [ + "Payload in raw cell " + ] }, { - "cell_type": "markdown", - "id": "2d42de4a", - "metadata": {}, - "source": [ - "" - ] + "cell_type": "markdown", + "id": "2d42de4a", + "metadata": {}, + "source": [ + "" + ] }, { - "cell_type": "code", - "execution_count": null, - "id": "b72e53fa", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": [""] - }, - "execution_count": null, - "metadata": {} - } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b72e63fa", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/png": [""] - }, - "execution_count": null, - "metadata": { - "filenames": { - "image/png": "\">" - } - } - } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b72e63f3", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/jpeg": [""] - }, - "execution_count": null, - "metadata": { - "filenames": { - "image/jpeg": "\">" - } - } - } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b72e635a", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/png": ["\">"] - }, - "execution_count": null, - "metadata": {} - } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "p72e635a", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/jpeg": ["\">"] - }, - "execution_count": null, - "metadata": {} - } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d72e635a", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/png": ["abcd"] - }, - "execution_count": null, - "metadata": { - "width": ">" - } - } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d72e095a", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/png": ["abcd"] - }, - "execution_count": null, - "metadata": { - "width": [">"] - } + "cell_type": "code", + "execution_count": null, + "id": "b72e53fa", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/svg+xml": [ + "" + ] + }, + "execution_count": null, + "metadata": {} + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72e63fa", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/png": [ + "" + ] + }, + "execution_count": null, + "metadata": { + "filenames": { + "image/png": "\">" } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae4f574d", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/png": ["abcd"] - }, - "execution_count": null, - "metadata": { - "height": ">" - } + } + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72e63f3", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/jpeg": [ + "" + ] + }, + "execution_count": null, + "metadata": { + "filenames": { + "image/jpeg": "\">" } - ], - "source": [""] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "w72e635a", - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "application/vnd.jupyter.widget-view+json": {"model_id": "wid1", "foo": "\"" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" } - ], - "source": [ - "import os; os.system('touch /tmp/pwned')" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "3616e107", - "metadata": {}, - "outputs": [ - { - "data": { - "text/markdown": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72e635a", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/png": [ + "\">" + ] + }, + "execution_count": null, + "metadata": {} + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "p72e635a", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/jpeg": [ + "\">" + ] + }, + "execution_count": null, + "metadata": {} + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d72e635a", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/png": [ + "abcd" + ] + }, + "execution_count": null, + "metadata": { + "width": ">" } - ], - "source": [ - "import os; os.system('touch /tmp/pwned')" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "4616e107", - "metadata": {}, - "outputs": [ - { - "data": { - "application/javascript": [ - "alert('application/javascript output')" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d72e095a", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/png": [ + "abcd" + ] + }, + "execution_count": null, + "metadata": { + "width": [ + ">" + ] } - ], - "source": [ - "import os; os.system('touch /tmp/pwned')" - ] - } + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae4f574d", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "image/png": [ + "abcd" + ] + }, + "execution_count": null, + "metadata": { + "height": ">" + } + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "w72e635a", + "metadata": {}, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "wid1", + "foo": "\"" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "\n", + "os.system('touch /tmp/pwned')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3616e107", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "\n", + "os.system('touch /tmp/pwned')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4616e107", + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "alert('application/javascript output')" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "\n", + "os.system('touch /tmp/pwned')" + ] + } ], "metadata": { "title": "TITLE", @@ -277,7 +324,12 @@ "version": "3.10.5" }, "widgets": { - "application/vnd.jupyter.widget-state+json": {"state": {"wid1": {}}, "foo": "pwntester