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

Проверяющий Python с инспектором тигра

Работа с несколькими разработчиками на большой кодовой базе может вызвать проблемы согласованности. Эти консистенции … с меткой Python.

Работа с несколькими разработчиками на большой кодовой базе может вызвать проблемы согласованности. Эти проблемы согласованности обычно обрабатываются во время отзывов кодов человека или линтами. Но в некоторых конкретных случаях встроенные обработчики, которые поставляются с LIMER, недостаточно недостаточно. Это где Инспектор Тигр Идея родилась.

Инспектор Тигр это структура обзора кода и линтер. Он поставляется с серией встроенных обработчиков (например, поиск Урожайки заявления, которые можно заменить на доходность от ). Но реальная цель использует свою рамку, разрабатывая плагины, специфичные кодовые базы. Это то, что мы собираемся построить через этот учебник.

class Foo(SomeObjects):
    def bar(self, x=[], y: Union[int, None] = None):
        x.append(1)
        for _ in range(3):
            try:
                super(bla, bla).foo_baz()
            except Exception:
                print("An exception")
            except AttributeError:
                print("An attribute error")
[Inspector Tiger] INFO - InspectorTiger inspected 🔎 and found these problems;
[Inspector Tiger] INFO -
[misc]
  - ../t.py:2:4     => DEFAULT_MUTABLE_ARG
  - ../t.py:5:12    => UNREACHABLE_EXCEPT
[upgradeable]
  - ../t.py:2:27    => OPTIONAL
  - ../t.py:6:16    => SUPER_ARGS

Как это работает

Как и большинство льготных инструментов, это статический анализатор. Он анализирует ваш код, не выполняя его и генерирует синтаксическое дерево из него. Для этого процесса мы используем Cpython’s AST модуль. После поколения AST плагины загружаются в Инспектор Отказ Сердце инспектора тигра. Каждый плагин предлагает Инспектор серия обработчиков. Обработчик представляет собой простую функцию, которая регистрирует себя в определенный узел AST. Когда этот узел приходит, Инспектор будет вызывать этот обработчик и получить Правда или Ложь Значение в соответствии с концепцией обработки.

Давайте начнем

Мы собираемся реализовать PEP 601 (который применяется к PEP 8 после отклонения). Речь идет о запрещении вернуть , Перерыв и Продолжить Заявления в наконец-то, наконец, где они откроются из окончательно.

Плагины в инспекторе Tiger являются пакетами Python (не модули). Для начала нам нужен планировка пакета Python (с Setup.py / setup.cfg ).

$ tree
├── pep601
│   ├── __init__.py
│   └── pep601.py
└── setup.py
from setuptools import setup, find_packages

setup(
    name="inspectortiger-pep601",
    packages=find_packages()
)

Как правило, мы называем наши плагины с Inspectortiger- Префикс, это просто дополнительный стандарт.

Прежде чем писать обработчик

Разработка обработчиков требует основного понимания узлов AST. Если вы не знакомы с AST, вы должны проверить Зеленые дерева змей Отказ Это «отсутствующие документы Python AST».

Сам обработчик

Мы начнем написать наше расширение, зарегистрировав наш обработчик в узел AST. Всякий раз, когда попробуйте / кроме / Наконец / еще Заявление приходит, Инспектор вызовут наш обработчик с АСТ. Попробуйте Узел и ссылка на внутреннюю базу данных, где обработчики могут делиться информацией между ними. Кстати, тщательно выберите имя функции, он будет использоваться в качестве кода ошибки.

@Inspector.register(ast.Try)
def control_flow_inside_finally(node, db):

Следующая вещь, которую мы собираемся сделать, это изучить, что такое АСТ. Попробуйте Узел выглядит как и продолжить.

>>> import ast
>>> code = """
def foo():
    try:
        foo()
    finally:
        return
"""
>>> tree = ast.parse(code)
>>> try_stmt = tree.body[0].body[0]

После получения заявления Try из функционального тела мы собираемся бросить его. Если вы не используете Python3.9 или выше, я предлагаю вам проверить астпрета Отказ

>>> print(ast.dump(try_stmt, indent=4))
Try(
    body=[
        Expr(
            value=Call(
                func=Name(id='foo', ctx=Load()),
                args=[],
                keywords=[]))],
    handlers=[],
    orelse=[],
    finalbody=[
        Return(value=None)])

Как видно, попробуйте узлы имеют Finaldobody Поле, которое содержит тело, наконец, заявления. Давайте повторять это Finaldobody и найти вхождения вернуть / Перерыв / Продолжать

    for subnode in node.finalbody:
        for child in ast.walk(subnode):

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

def invalid():
    try:
        pass
    finally:
        return

def valid():
    try:
        pass
    finally:
        def foo():
            return

Для поиска таких вещей нам нужно знать, к какому контексту есть узел. К счастью Инспектор Тигр поставляется со встроенным Управление контекстом плагин. Давайте попробуем это, создав элемент управления, если узел дочернего узла является оператором возврата, и контекст узера дочернего узела одинаково с контекстом нашей попытки/наконец-то оператора.

            if isinstance(child, ast.Return) and get_context(
                child, db
            ) is get_context(node, db):
                return True

И если это так, мы вернем что-то, что не является фальсивом. Второй чек, если ребенок является Продолжить или Перерыв утверждение И это не внутри другого для петля. Этот пример может дать вам лучшее понимание того, какой случай действителен, какой случай не

def invalid():
    for _ in baz:
        try:
            pass
        finally:
            continue

def valid():
    for _ in baz:
        try:
            pass
        finally:
            for _ in bar:
                continue

Для такого проверки нам нужно пройти родителей нашего узла на окончательно и проверить, есть ли другой для петли. И, к счастью, снова у нас есть плагин для проезжает родителей . паритете предлагает полезность по имени parent_to Отказ Это даст каждому родителю от Ребенок Узел к данному родительскому узлу. Если нет вхождений A для цикла между Продолжить / перерыв И наше заявление TRY Можно сказать, что утверждение выйдет из окончательно.

            elif isinstance(child, (ast.Break, ast.Continue)) and not any(
                isinstance(parent, ast.For)
                for parent in parent_to(child, node)
            ):
                return True

И это было, мы закончили.

Конфигурация

Последнее, что настраивает .inspector.rc. В вашем домашнем каталоге и указания вашего плагина.

{
    "plugins": {
        "pep601": ["pep601"],
        "package": ["module_that_contains_handlers"]
    }
}

Оригинал: “https://dev.to/btaskaya/inspecting-python-with-inspector-tiger-3hfb”