2014-02-21 3 views
7

В моей рабочей среде мы создаем большое количество пакетов Python для внутреннего использования (10 с, если не 100). Каждый пакет имеет некоторые зависимости, как правило, от сочетания внутренних и внешних пакетов, и некоторые из этих зависимостей являются общими.Проверка совместимости API Python

По мере приближения dependency hell, обновления зависимостей становятся трудоемким процессом. Хотя мы заботимся о функциональных изменениях, которые может внести новая версия, равной (если не больше) важности - это изменения API, которые нарушают код.

Несмотря на то, что тесты на совместимость с более новыми версиями зависимостей помогают нам выявить некоторые проблемы, наше покрытие недостаточно близко к 100%, чтобы сделать эту стратегию надежной. Примечания к выпуску и журнал изменений помогают выявить серьезные изменения на высоком уровне, но они редко существуют для встроенных инструментов или достаточно подробно для понимания последствий, которые новая версия имеет для (публичного) API.

Я смотрю на другие пути, чтобы автоматизировать этот процесс.

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

В другом месте мы используем C++ abi-compliance-checker и смотрим на Java api-compliance-checker, чтобы помочь в этом процессе. Есть ли аналогичный инструмент для Python? Я нашел много инструментов для анализа/анализа/рефакторинга, но ничто из того, что обеспечивает этот уровень функциональности. Я понимаю, что динамическая типизация Python сделает всеобъемлющий отчет невозможным.

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

Редактировать: после размещения вопроса я нашел pysdiff, который охватывает некоторые из моих требований, но заинтересован в том, чтобы видеть альтернативы по-прежнему.

Редактировать: также найдено Upstream-Tracker будет хорошим примером такой информации, которую я хотел бы получить.

ответ

2

Может быть, вы можете начать с помощью модуля

import inspect 
import types 
def genFunctions(module): 
    moduleDict = module.__dict__ 
    for name in dir(module): 
     if name.startswith('_'): 
      continue 
     element = moduleDict[name] 
     if isinstance(element, types.FunctionType): 
      argSpec = inspect.getargspec(element) 
      argList = argSpec.args 
      print "{}.{}({})".format(module.__name__, name, ", ".join(argList)) 

inspect Это даст вам список «общественности» (не начиная с подчеркиванием) функции с их списками аргументов. Вы можете добавить больше материала для печати kwargs, классов и т.д.

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

myPackage.myModule.myFunction1(foo, bar) 
myPackage.myModule.myFunction2(baz) 

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

+1

Этот подход выглядит довольно простым и будет достаточно простым, чтобы распространяться на типы обложек.ClassType и types.MethodType и т. Д. Но я бы предпочел не импортировать проверяемый модуль (я должен был поставить это в исходный вопрос). Я думаю, что статический анализ был бы предпочтительнее, поскольку он упрощает среду выполнения для сравнения (я думаю, pylint работает, например, это, например). –

0

Проверьте zope.interfaces (вы можете получить его из PyPI). Затем вы можете включить модульное тестирование, чтобы модули поддерживали интерфейсы в ваших модульных тестах. Может потребоваться некоторое время для ретро-адаптации, но это не серебряная пуля.

+0

Итак, идея состоит в том, чтобы обернуть каждую зависимость в объекте zope.interface, который является частью набора тестов приемного модуля, который я запускаю для новых версий? Похоже, что это будет очень хорошо работать для проектов с новыми проектами, но я думаю, что это займет слишком много времени. –

+0

Да. Я думаю, что это довольно хорошее резюме. Я предполагаю, что я предлагаю вам оторвать большую часть существующего кода за интерфейсами. Я еще раз выстрелю в ответ :) –

5

Как использовать модуль AST для анализа файлов?

import ast 

with file("test.py") as f: 
    python_src = f.read() 

    node = ast.parse(python_src) # Note: doesn't compile the src 
    print ast.dump(node) 

Там в метод ходьбы от узла AST (описанного http://docs.python.org/2/library/ast.html)

astdump может работать (доступно на PyPI)

Это устаревшее довольно принтер http://code.activestate.com/recipes/533146-ast-pretty-printer/

Документация, Инструмент Sphinx также извлекает информацию, которую вы ищете. Возможно, посмотрите.

Итак, пройдите AST и постройте дерево с информацией, которую вы хотите в нем. После того, как у вас есть дерево, вы можете разложить его и позже разложить или преобразовать дерево в текстовое представление в текстовом файле , который вы можете разграничить с помощью diffftools или некоторой внешней программы diff.

Аст имеет методы parse() и compile(). Единственное, что я не совсем уверен, сколько информации доступно вам после разбора (поскольку вы не хотите компилировать()).

Смежные вопросы