Поиск изменений интерфейса путем сравнения файлов заглушек Python
Я пытаюсь определить изменения интерфейсов открытых частей пакета python по сравнению с предыдущей версией. Это поможет с семантическим версионированием (MAJOR_CHANGE.MINOR_CHANGE.PATCH). Лучший способ, которым я могу придумать, это сделать следующее:
- Создайте заглушки для новой версии пакета (вы можете сделать это с помощью stubgen. Это дает вам открытый интерфейс для всего пакета, подпакетов и модулей...
- Сравните заглушки для новой и старой версии пакета.
- Если нет никаких изменений интерфейса, то новая версия будет просто +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 из старых и новых реализаций, чтобы найти потенциальные критические изменения за существующими интерфейсами!