Перенос кода Perl на Python

Способы миграции унаследованного не покрытого тестами кода Perl на Python

Перенос унаследованного кода Perl на Python может быть весьма непростой задачей. В данной статье вы познакомитесь с некоторыми теоретическими аспектами работы с унаследованным кодом, в том числе узнаете, чего делать не стоит.

Ной Гифт, разработчик программного обеспечения, IBM  

Photo of Noah GIftНой Гифт (Noah Gift) – соавтор книги "Python For Unix and Linux" издательства O'Reilly. Он также является автором, докладчиком, консультантом и лидером сообщества, пишущим публикации для IBM developerWorks, Red Hat Magazine, O'Reilly и MacTech. Адрес сайта его консалтинговой компании - www.giftcs.com, адрес его персонального сайта - www.noahgift.com. В настоящий момент Ной также поддерживает сайт www.pyatl.org – место общения пользователей, работающих с Python, из Атланты, Джорджия. Он окончил университет штата Калифорния, получив степень магистра в области информационных компьютерных систем, а также окончил политехнический университет штата Калифорния, расположенный в Сан-Луис-Обиспо, получив степень бакалавра в области пищевых наук. Он также является сертифицированным системным администратором Apple и LPI, имеет опыт работы в таких компаниях, как Caltech, Disney Feature Animation, Sony Imageworks и Turner Studios. Все свое свободное время он проводит со своей женой Лией (Leah) и их сыном Лиамом (Liam), играя на пианино и духовно совершенствуясь.



27.04.2011

Я начну с цитаты из книги Дэмиана Конвея Передовые приемы работы с Perl: "Поддержка ООП в Perl реализована, пожалуй, чересчур в духе Perl: есть слишком много способов сделать это... Существует настолько много возможных комбинаций реализации, структуры и семантики, что в Perl сложно встретить две независимые иерархии классов, использующие в точности один и тот же стиль ООП".

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

Если вы приняли такое решение, нужно выбрать оптимальную стратегию решения проблемы. Если вам повезло иметь в своем распоряжении хорошо написанный объектно-ориентированный код Perl, с полным покрытием кода тестами, то, вполне возможно, вам нужно будет просто перенести юнит-тесты с Perl на Python, а затем добиться того, чтобы соответствующий код Python проходил перенесенные на Python юнит-тесты. Хотя есть много талантливых Perl-программистов, которые пишут хорошо документированный и легко читаемый код, все же они встречаются не так часто, как хотелось бы. Скорее всего вы окажетесь в ситуации, когда вы видите, что ваш код Perl работает, однако не имеете понятия о том, как именно он работает. Именно здесь начинаются трудности переноса кода с Perl на Python.

Не следует вызывать код Perl из вашего нового кода Python

Автоматическая генерация тестов в Python

Программистам на Python не следует зазнаваться. Хотя многие согласны с тем, что Python спроектирован таким образом, что позволяет писать легко читаемый код, но тем не менее унаследованный не покрытый тестами код Python может также доставлять проблемы. В конце концов Python сам себя не тестирует… или тестирует?

Одним из возможных способов решения проблемы унаследованного не покрытого тестами кода Python является использование инструмента генерации тестов Pythoscope. Идея Pythoscope формулируется так: "создать легко настраиваемый и расширяемый инструмент с открытым кодом, который будет автоматически или полуавтоматически генерировать юнит-тесты для унаследованных систем, написанных на Python". Помимо этого, можно задать точки входа, которые будут автоматически тестировать функции в зависимости от их выполнения. В ссылках из раздела Ресурсы можно найти подробный пример того, как это работает.

Прежде чем перейти к рекомендуемым подходам, давайте сначала посмотрим, чего не стоит делать. Человеку при столкновении с проблемой свойственно выбирать путь наименьшего сопротивления. Перенос долгое время органично развивавшегося и не покрытого тестам кода Perl на Python – это сложная задача, поэтому самым очевидным решением может показаться поиск способа избежать переписывания всего кода Perl. Такой ход мысли может в итоге привести вас к модулю perlmodule, который позволяет встроить интерпретатор Perl в Python, чтобы ограничиться вызовом старого кода Perl из нового кода Python.

Это плохая идея, так как в итоге вы будете иметь еще большую проблему, чем та, с которой вы начинали. У вас останется унаследованный код, который вы не понимаете, а также будет новый код, вызывающий код, который вы не понимаете. Это похоже на выплату платежа по одному кредиту за счет денег, полученных по другому кредиту – вы лишь отсрочиваете неизбежное и увеличиваете свой технический долг (см. подробную информацию о техническом долге по ссылке в разделе Ресурсы). Усугубляет ситуацию еще и то, что вы «инфицируете» свой новый код, включая в него трудноуловимые ошибки, которые сложно или невозможно покрыть тестами. В конечном счете новым разработчикам, которые потом придут в проект, придется работать с кодом, представляющим собой пугающую смесь непротестированного кода Perl и неадекватно протестированного кода Python.


Функциональное тестирование унаследованного кода с помощью nose для создания новой спецификации

В книге Working Effectively With Legacy Code автор Майкл Физерс пишет: "одна из вещей, которые замечают практически все, пытающиеся написать тесты для существующего кода, - это то, насколько плохо код подходит для тестирования". Возможно, вы тоже обратите на это внимание, впервые задумавшись о переносе унаследованного не покрытого тестами кода Perl на Python.

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

Для выполнения следующего примера вам нужно будет установить пакет nose. Если у вас уже установлен инструмент easy_install для быстрой установки пакетов Python, можно просто выполнить команду easy_install nose. Если нет, вы можете сначала установить setuptools, следуя инструкциям по установке setuptools.

Вот пример простого теста nose:

Листинг 1. Заведомо неудачный тест nose
#!/usr/bin/env python
"""Первый этап переноса Perl на Python"""

import os

def test_script_exists():
    """Этот тест заведомо завершится неудачно"""
    assert os.path.exists("myreport.csv")

Результат работы этого теста выглядит так:

Листинг 2. Результат работы теста
linux% /usr/local/bin/nosetests
F
===================================================
FAIL: test_failing_functional.test_script_exists
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Python/2.5/site-packages/nose-0.10.4-py2.5.egg/nose/case.py", 
    line 182, in runTest 
      self.test(*self.arg)
  File "/usr/home/ngift/tests/test_failing_functional.py", line 7, in test_script_exists
    assert os.path.exists("myreport.csv")
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.037s

FAILED (failures=1)

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

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

Последним и обычно самым сложным шагом является написание кода Python, проходящего созданные вами тесты. К сожалению, для данной задачи не существует серебряной пули. Перенос унаследованного не покрытого тестами кода, написанного на Perl или на любом другом языке, – это сложная задача. Написание неудачно выполняющихся тестов – это разумная стратегия, которая может оказаться очень полезной при решении этой задачи.


Заключение

Я закончу цитатой из статьи Гвидо ван Россума "Strong Versus Weak Typing": "Вы никогда не выловите все ошибки. Сделать код легче для чтения и написания и понятнее для людей, которые будут его просматривать, может оказаться гораздо полезней...."

В конце концов создание читаемого, доступного для тестирования кода является одной из главных задач при переносе унаследованного кода на новый язык, такой как Python. Если вы поставите себе такую цель, вам будет гораздо легче выполнить задачу. Удачи!

Ресурсы

Научиться

Получить продукты и технологии

  • Знакомьтесь с продуктами IBM наиболее подходящим для вас способом: загрузите ознакомительную версию продукта, поработайте с продуктом в онлайновом режиме, используйте продукт в облачной среде или проведите несколько часов в SOA Sandbox, чтобы узнать об эффективном создании сервис-ориентированных архитектур.(EN)

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=651066
ArticleTitle=Перенос кода Perl на Python
publish-date=04272011