Рубрики
Без рубрики

Создание магической функции проверки типа в iPython

Как запустить JavaScript или Ruby-код, не оставив iPython? Как создать проверку типа, который будет использовать MyPy для проверки блоков кода в iPython? Эти и другие вопросы отвечают во второй части серии IPython Magic Functions. Помечено с iPython, Python.

Магические магии в iPython

В Предыдущий пост Я объяснил, что являются волшебными функциями и почему они крутые. Мы также создали линия волшебства Функция, которая интерпретирует математические формулы, написанные в польском языке. Сегодня мы поговорим о клеточная магия Функции.

Грады клеток похожи на линию магии, за исключением того, что они работают на клетках (блоки кода), а не на одном строках. IPython поставляется с несколько предопределенных И большинство из них позволит вам интерпретировать код, написанный на другом языке программирования. Нужно запустить код Python 2, но iPython использует Python 3 по умолчанию? Нет проблем, просто введите %% Python2 , вставьте/введите код и запустите его:

In [1]: print 'hello there'
  File "", line 1
    print 'hello there'
                      ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print('hello there')?

# But!

In [2]: %%python2
   ...: print 'hello there'
   ...:
   ...:
hello there

Вы также можете запустить код, написанный в Ruby , Bash , JavaScript и другие языки. И эти разные блоки кода могут взаимодействовать друг с другом, например Вы можете запустить какой-нибудь код JavaScript и отправлять переменные обратно в Python .

Написание функции клеточной магии

Теперь, давайте попробуем написать нашу собственную магическую функцию клеток. Я изначально хотел продолжить с примером польской обозначения из первой части серии. Поэтому я начал писать функцию, которая переводит все математические операции в блоке кода в польскую форму обозначения. К сожалению, я быстро понял, что если я хочу написать хороший пример (не какой-то код, который работает только для + и - ), Мне пришлось бы написать правильный переводчик. И это больше не будет простым примером 1 Отказ Так что на этот раз мы собираемся сделать что-то другое.

Одна из новых функций, которые пришли в Python в версии 3.5, являются Тип подсказки . Некоторые такие люди, как они, некоторые люди не (которые, вероятно, верны для каждый Новая особенность в каждый язык программирования). Приятная вещь о подсказках типа Python в том, что они не являются обязательными. Если вам они не нравятся – не используйте их. Для быстрой прототипирования или проекта, который вы поддерживаете себя, вы, вероятно, отлично без них. Но для большой кодовой базы с большим количеством устаревших кодов, поддерживаемых несколькими разработчиками – типа подсказки могут быть чрезвычайно полезными!

Как вы, вероятно, начинаете догадаться, наша функция Magic Magic проверит типы для блока кода. Почему? Ну, с iPython, вы можете быстро прототировать какой-то код, настраивать его и сохранить его в файл, используя %спасти или %% writefile Волшебные функции (или просто скопируйте и вставьте его, если это быстрее для вас). Но на момент написания этой статьи в Python нет встроенного типа проверки типа. Marpy Библиотека – это де-факто Статический тип Checker, но это все еще внешний инструмент, который вы запускаете из Shell ( MyPy filename.py ). Итак, давайте сделаем помощник, который позволит нам набрать проверить код Python прямо в iPython!

Вот как мы ожидаем, что это работать:

In [1]: %%mypy
   ...: def greet(name: str) -> str:
   ...: return f"hello {name}"
   ...: greet(1)
   ...:
   ...:
Out[1]: # It should print an error message, as 1 is not a string

Для этого мы просто позвоним Беги Функция от mypy.api (как предложено в документации ) и пройти -C Program_Text Параметр, который проверяет строку Отказ

Вот код для проверки типа:

from IPython.core.magic import register_cell_magic

@register_cell_magic('mypy')
def typechecker(line, cell):
    try:
        from mypy.api import run
    except ImportError:
        return "'mypy' not installed. Did you run 'pip install mypy'?"

    args = []
    if line:
        args = line.split()

    result = run(['-c', cell, *args])

    if result[0]:
        print('\nType checking report:\n')
        print(result[0]) # stdout

    if result[1]:
        print('\nError report:\n')
        print(result[1]) # stderr

    # Return the mypy exit status
    return result[2]

Давайте пройдемся через код, учитывая, что есть несколько интересных битов:

@register_cell_magic(mypy)
def typechecker(line, cell):

Начнем с определения функции под названием TypeChecker и регистрация его в виде функции клеточной магии под названием %% Mypy Отказ Почему я не просто определил функцию под названием Marpy Вместо того, чтобы делать это переименование? Ну, если бы я сделал это, то наше Marpy Функция будет Тень Marpy модуль. В этом случае, вероятно, не приведут никаких проблем. Но в целом, вам следует избегать переменных/функций/модулей/модулей, потому что однажды он приведет вас к тому, что вам больно головная боль.

try:
    from mypy.api import run
except ImportError:
    return "`mypy` not found. Did you forget to run `pip install mypy`?"

Внутри нашей функции мы сначала пытаемся импортировать Marpy модуль. Если он не доступен, мы сообщаем пользователю, что он должен быть установлен, до того, как эта волшебная функция может быть использована. Хорошая вещь о импорте Marpy в TypeChecker Функция состоит в том, что ошибка импорта будет отображаться только при запуске волшебной функции. Если вы поместите импорт в верхней части файла, а затем сохраните файл внутри каталога запуска iPython, а вы Не есть майка Установлен модуль, вы получите ImporteRor Каждый раз, когда вы начинаете iPython. Недостатком этого подхода является то, что вы используете код импорта каждый раз, когда вы запускаете TypeChecker функция. Это то, что вы должны избегать делать, если вы заботитесь о производительности, но в случае нашего маленького помощника это не большая проблема.

Если вы используете Python 3.6 или выше, вы можете поймать ModulenotfoundError Ошибка вместо ImporteRor Отказ ModulenotfoundError это Новый подкласс ImporteRor брошенный, когда модуль не может быть расположен . Я хочу сохранить свой код, совместимый с более низкими версиями Python 3, поэтому я придерживаюсь ImporteRor Отказ

args = []
if line:
    args = line.split()

result = run(['-c', cell, *args])

Обратите внимание, что функция, используемая для определения клеточной магии, должна принять как линия и клетка параметр. Что отлично, потому что таким образом, мы можем передать параметры на Marpy Действительно Так вот здесь мы проходим дополнительные аргументы из линия Параметр на Беги функция. Вот как вы могли бы запустить нашу волшебную функцию с разными настройками:

In [1]: %%mypy --ignore-missing-imports --follow-imports error
   ...: CODEBLOCK

что эквивалентно запустить следующую команду в командной строке: Mypy --ignore - отсутствует - импорт Imports --follow-imports ошибка -C 'CodeBlock' Отказ

Остальная часть кода очень похожа на Пример из документации Отказ

Время тестирования!

Наша клеточная волшебная функция готова. Давайте сохраним его в каталоге запуска iPython ( Какой каталог Startup iPython? ), так что он будет доступен в следующий раз, когда мы начнем iPython. В моем случае я экономлю его в файле под названием:

~/.ipython/profile_default/startup/magic_functions.py

Теперь давайте поджегми амифоном и посмотрим, работает ли это:

In [1]: %%mypy
   ...: def greet(name: str) -> str:
   ...: return f"hello {name}"
   ...: greet('Bob')
   ...:
   ...:
Out[1]: 0

In [2]: %%mypy
   ...: def greet(name: str) -> str:
   ...: return f"hello {name}"
   ...: greet(1)
   ...:
   ...:

Type checking report:

:3: error: Argument 1 to "greet" has incompatible type "int"; expected "str"

Out[2]: 1

Отлично, это работает! Он возвращает 0 (который является стандартным кодом выхода UNIX для успешной команды), если все в порядке. В противном случае он сообщает о том, какие проблемы были найдены.

Как насчет передачи некоторых дополнительных параметров?

In [3]: %%mypy
   ...: import flask
   ...:
   ...:

Type checking report:

:1: error: No library stub file for module 'flask'
:1: note: (Stub files are from https://github.com/python/typeshed)

Out[3]: 1

# Ok, this can happen (https://mypy.readthedocs.io/en/latest/running_mypy.html#ignore-missing-imports)
# Let's ignore this error

In [4]: %%mypy --ignore-missing-imports
   ...: import flask
   ...:
   ...:
Out[4]: 0

Прохождение дополнительных параметров также работает!

Отлично, мы создали хорошую функцию вспомогательного помощника, которую мы можем использовать для проверки, если подсказки типа верны в данном блоке кода.

Функция линии линии и клеточной магии

Есть еще один декоратор, который мы еще не обсуждали: @register_line_cell_magic Отказ Это ничего особенного – особенно сейчас, когда вы знаете, как работает Line Magic и Magic Magic – так что нет необходимости в отдельной статье. Документация по iPython Очень хорошо объясняет этот декоратор:

@register_line_cell_magic
def lcmagic(line, cell=None):
    "Magic that works both as %lcmagic and as %%lcmagic"
    if cell is None:
        print("Called as line magic")
        return line
    else:
        print("Called as cell magic")
        return line, cell

Если вы запустите % lcmagic Эта функция не будет получать клетка Параметр, и он будет действовать как линия магии. Если вы запустите %% lcmagic это получит клетка Параметр и – необязательно – линия Параметр (например, в нашем последнем примере с %% Mypy ). Таким образом, вы можете проверить наличие клетка Параметр и на основе этого, контроль, если он должен действовать как линию или клеточную магию.

Вывод

Теперь вы знаете, как сделать линия волшебства и а клеточная магия функции и как объединить их вместе в линия и магия функция. Есть еще одна особенность, которые предлагает iPython – Магический класс . Это позволяет писать более мощные волшебные функции, так как они могут, например, удерживать состояние между вызовами. Так что оставайся настроенными для Последняя часть этой статьи Действительно

Изображение из: Пэтэльс

  1. Написание переводчика все еще отличное упражнение! Я недавно последовал за Давайте построим простой переводчик Серия, где вы построили интерпретатор Паскаля в Python, и это был действительно забавный проект для того, кто никогда не изучал компиляторы. Итак, если вы заинтересованы в этом типе вызова, этот блог может помочь вам начать. ↩

Оригинал: “https://dev.to/switowski/creating-a-type-checker-magic-function-in-ipython-i1j”