Поиск изменений интерфейса путем сравнения файлов заглушек Python

Я пытаюсь определить изменения интерфейсов открытых частей пакета python по сравнению с предыдущей версией. Это поможет с семантическим версионированием (MAJOR_CHANGE.MINOR_CHANGE.PATCH). Лучший способ, которым я могу придумать, это сделать следующее:

  1. Создайте заглушки для новой версии пакета (вы можете сделать это с помощью stubgen. Это дает вам открытый интерфейс для всего пакета, подпакетов и модулей...
  2. Сравните заглушки для новой и старой версии пакета.
    • Если нет никаких изменений интерфейса, то новая версия будет просто +1 к номеру PATCH
    • Если исходный интерфейс остается прежним, но добавляются все новые функции (или атрибуты), это будет незначительным изменением, поэтому +1 к МИНОРУ
    • Если какая-либо часть исходного интерфейса будет изменена, это будет ОСНОВНОЕ критическое изменение, так что +1 к ОСНОВНОМУ

В общем, вопрос в том, каков наилучший способ сравнить 2 пакета файлов-заглушек для этих изменений? Мы могли бы сравнить файлы AST, хотя они не включают информацию о типе (мы могли бы попытаться использовать это, чтобы обойти это https://github.com/python/typed_ast)

Пример файла заглушки здесь:

# Stubs for positioning.point (Python 3.6)
#
# NOTE: This dynamically typed stub was automatically generated by stubgen.

import numpy as np
from .exceptions import UnacceptableCartesianCoordinates
from .frame import Frame
from .methods import check_frames_have_common_parent, get_coordinates_of_point_in_frame, lowest_common_parent
from typing import Any

class Point:
    def __init__(self, frame: Frame, point_coordinates: np.ndarray) -> None: ...
    @classmethod
    def from_cartesian(cls: Any, frame: Frame, x: float, y: float, z: float) -> Point: ...
    @classmethod
    def from_cylindrical(cls: Any, frame: Frame, r: float, phi: float, z: float) -> Point: ...
    @classmethod
    def from_spherical(cls: Any, frame: Frame, r: float, theta: float, phi: float) -> Point: ...
    @classmethod
    def from_old_point_in_new_frame(cls: Any, old_point: Point, new_frame: Frame) -> Point: ...
    @classmethod
    def at_origin(cls: Any, frame: Frame) -> Point: ...
    @property
    def frame(self): ...
    def __eq__(self, other: Any) -> bool: ...

РЕДАКТИРОВАТЬ:

Чтобы быть более ясным (и с причудливыми диаграммами!), Я хочу сравнить публичные интерфейсы между 2 пакетами, A и B. Лучший способ создать общедоступный интерфейс с информацией о типах - это использовать stubgen. Итак, открытый интерфейс для пакетов будет определяться пакетом .pyi файлы.

Затем мы используем новый магический инструмент (называется что-то вроде быстрого interface-diff) сравнить оба интерфейса для A и B. На самом высоком уровне у нас будет 4 потенциальных результата при сравнении интерфейсов.

1) ОСНОВНЫЕ - Вещи, удаленные из А, а НЕ вещи, добавленные в В

2) ОСНОВНЫЕ - Вещи, удаленные из А И вещи, добавленные в В

3) МЕНЬШЕЕ - Вещи, добавленные в B, а НЕ удалены из A

4) патч - A == B

Рад написать все это сам, если кто-то может поставить меня на правильные строки:)

1 ответ

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

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

Другие вопросы по тегам