{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ " import ipylintotype, IPython, traitlets, abc, ipykernel, mypy.api, io, contextlib, re, pylint.lint\n", " from tempfile import TemporaryDirectory\n", " from pathlib import Path\n", " get_ipython = IPython.get_ipython\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ " class Annotator(traitlets.HasTraits):\n", " mimetype = traitlets.Unicode()\n", " entry_point = traitlets.Unicode()\n", " def __call__(Annotator, code, all_code, metadata, *args, **kwargs):\n", " return Annotator.run(\n", " code, all_code.get(Annotator.mimetype, []), metadata.get(\n", " Annotator.mimetype, {}\n", " ).get(Annotator.entry_point), *args, **kwargs)\n", " @traitlets.default('entry_point')\n", " def _default_entry_point(Annotator): return Annotator.__name__\n", " @abc.abstractmethod\n", " def run(Annotator):\n", " raise NotImplementedError()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ " class IPythonAnnotator(Annotator):\n", " mimetype = traitlets.Unicode(default_value='text/x-ipython')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ " _re_mypy = (r\"(?P.*):(?P\\d+):(?P\\d+):\\s*(?P.*)\\s*:(?P.*)\")\n", "\n", " class MyPy(IPythonAnnotator):\n", " args = traitlets.Unicode(\"--show-column-numbers --show-error-context --follow-imports silent\")\n", " entry_point = traitlets.Unicode(default_value=mypy.__name__)\n", " def run(MyPy, code, all_code=None, metadata=None, *args):\n", " args = list(args) + MyPy.args.split() + [\"-c\", code] \n", " out, err, count = mypy.api.run(args)\n", " lines = [line for line in out.strip().split(\"\\n\")]\n", " matches = [re.match(_re_mypy, line) for line in out.strip().split(\"\\n\")]\n", " matches = [match.groupdict() for match in matches if match]\n", " return [\n", " {\n", " \"message\": f\"\"\"{err[\"message\"]} [mypy]\"\"\",\n", " \"severity\": err[\"severity\"],\n", " \"from\": dict(line=int(err[\"line\"]) - 1, col=int(err[\"col\"]) - 1),\n", " \"to\": dict(line=int(err[\"line\"]) - 1, col=int(err[\"col\"])),\n", " }\n", " for err in matches\n", " ]\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ " class PyLint(IPythonAnnotator):\n", " entry_point = traitlets.Unicode(default_value=pylint.__name__)\n", " def run(PyLint, code, all_code=None, metadata=None, *args):\n", " with TemporaryDirectory() as td:\n", " tdp = Path(td)\n", " fn = tdp / \"foo.py\"\n", " fn.write_text(code)\n", " s = io.StringIO()\n", " with contextlib.redirect_stdout(s), contextlib.redirect_stderr(io.StringIO()):\n", " try:\n", " res = pylint.lint.Run([str(fn)])\n", " except:\n", " pass\n", " matches = re.findall(\n", " r'^(.):\\s*(\\d+),\\s(\\d+):\\s*(.*?)\\s*\\((.*)\\)$', \n", " s.getvalue(),\n", " flags=re.M\n", " )\n", "\n", "\n", " return [\n", " {\n", " \"message\": msg,\n", " \"severity\": {\n", " \"W\": \"warning\",\n", " #\"C\": \"convention\"\n", " }.get(severity, \"error\"),\n", " \"from\": dict(line=int(line) - 1, col=int(col)),\n", " \"to\": dict(line=int(line) - 1, col=int(col) + 1),\n", " }\n", " for severity, line, col, msg, rule in matches\n", " ]" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ " class AnnotationFormatter(IPython.core.interactiveshell.InteractiveShell):\n", " formatters = traitlets.List()\n", " \n", " comm_name = traitlets.Unicode(default_value=ipylintotype._version.__comm__)\n", " current_comm = traitlets.Any()\n", " def init_user_ns(AnnotationFormatter): ...\n", " def __init__(AnnotationFormatter, *args, **kwargs):\n", " if 'parent' in kwargs: kwargs = {**kwargs['parent']._trait_values, **kwargs}\n", " super().__init__(*args, **kwargs)\n", " AnnotationFormatter.init_comm()\n", " \n", " def init_comm(AnnotationFormatter):\n", " AnnotationFormatter.close()\n", " AnnotationFormatter.current_comm = ipykernel.comm.Comm(target_name=AnnotationFormatter.comm_name)\n", " AnnotationFormatter.current_comm.on_msg(AnnotationFormatter.on_msg)\n", " __import__('atexit').register(AnnotationFormatter.close)\n", "\n", "# def __call__(AnnotationFormatter, code, *args, all_code=None, metadata=None):\n", "# result = dict()\n", "# for formatter in AnnotationFormatter.formatters:\n", "# if formatter.mimetype in all_code:\n", "# result.setdefault(formatter.mimetype, list).extend(\n", "# formatter(code, *args, all_code=all_code, metadata=metadata))\n", "# return result\n", "\n", " def __call__(AnnotationFormatter, code, all_code=None, metadata=None, *args):\n", " result = []\n", " code = AnnotationFormatter.transform_cell(code)\n", " for formatter in AnnotationFormatter.formatters:\n", " AnnotationFormatter.log.warn(\"CALL %s\", args)\n", " result.extend(formatter(code, all_code=all_code, metadata=metadata, *args))\n", " return result\n", "\n", " def close(AnnotationFormatter): \n", " if AnnotationFormatter.current_comm: AnnotationFormatter.current_comm.close()\n", " AnnotationFormatter.current_comm = None\n", "\n", " def on_msg(AnnotationFormatter, msg):\n", " AnnotationFormatter.log.warn(\"MSG %s\", msg)\n", " data = msg[\"content\"][\"data\"]\n", " annotations = AnnotationFormatter(data.get(\"code\", \"\"), all_code={}, metadata={})\n", " AnnotationFormatter.log.warn(\"ANNO %s\", annotations)\n", " AnnotationFormatter.current_comm.send(dict(id=data[\"id\"], annotations=annotations))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ " FORMATTER = None\n", " def load_ipython_extension(shell):\n", " global FORMATTER\n", " unload_ipython_extension(shell)\n", " FORMATTER = AnnotationFormatter(formatters=[MyPy(), PyLint()], parent=get_ipython()) \n", " def unload_ipython_extension(shell):\n", " global FORMATTER\n", " if FORMATTER: FORMATTER.close()\n", " if __name__ == '__main__':\n", " load_ipython_extension(get_ipython())" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "x = 1\n", "y = x + '9'" ] } ], "metadata": { "kernelspec": { "display_name": "p6", "language": "python", "name": "other-env" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" } }, "nbformat": 4, "nbformat_minor": 2 }