PyFence: верификация типов для Python



    PyFence — самопальная утилита-библиотека, которая позволяет следить за соответствием типов во время отладки вашего проекта. PyFence берет информацию о типах из docstring'ов функций в стандартном формате Sphinx. То есть, если у вас уже есть документация, больше ничего делать для использования PyFence не нужно!

    Например, возьмем следующий класс:


    class RationalFormatter (object):
        def format(self, number):
            """
            Stringifies a number to numerator/denominator format
            Example::
    
                >>> print(RationalFormatter().format(1.25))
                5/4
    
            :param number: input number
            :type  number: float
            :raises      : None
            :rtype       : str
            """
            return '%i/%i' % number.as_integer_ratio()
    
        def display(self, number):
            print(str(number) + ' = ' + self.format(number))
    


    Метод format представляет число в виде дроби, используя метод float.as_integer_ratio()

    Пробуем:

    >>> from formatter import RationalFormatter
    >>> f = RationalFormatter()
    >>> f.display(1.25)
    1.25 = 5/4
    


    Вроде работает? Однако все сломается, если передать int, ведь int, к сожалению, .as_integer_ratio() не содержит.

    >>> f.display(5)
    Traceback (most recent call last):
      File "example.py", line 5, in <module>
        f.display(5)
      File "/home/eugeny/Work/pyfence/example_formatter.py", line 18, in display
        print(str(number) + ' = ' + self.format(number))
      File "/home/eugeny/Work/pyfence/example_formatter.py", line 15, in format
        return '%i/%i' % number.as_integer_ratio()
    AttributeError: 'int' object has no attribute 'as_integer_ratio'
    


    Впрочем, при использовании PyFence такую проблему можно было бы заметить гораздо раньше:

    $ pip install pyfence
    ...
    $ fence example.py --fence:strict,stop
    1.25 = 5/4
     *** PyFence ERROR ---------------------------
     *** PyFence ERROR PyFence verification failed
     *** PyFence ERROR  :: example_formatter.RationalFormatter.format(<example_formatter.RationalFormatter object at 0x7ff097a47d10>, 5)
     *** PyFence ERROR in example_formatter.py:2
     *** PyFence ERROR number was 5 (int) instead of ['float']
     *** PyFence ERROR Aborting
    


    Кроме того, PyFence может проверять типы возникающих в функциях/методах исключений и возвращаемые типы.
    Можно также импортировать модуль pyfence в самом проекте, в этом случае не придется использовать отдельную утилиту fence.
    Разумеется, использовать fence стоит только при разработке, а не в продакшне, так как из-за проверок возможно падение производительности.

    Я буду очень благодарен любому фидбеку и Pull Request'ам!
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 18
    • +1
      В PyCharm есть аналогичный инспектор, сильно помогает. Случайно не ваш?
      Поддерживает ли PyFence множественные варианты с ":type obj: :class:`project.entities.MyClass` or None" и inline-нотацию типа ":param int number: description"? У PyCharm со вторым проблемы.
      • 0
        Множественные поддерживаются через запятую (см. документацию, в ней примеры). Насколько я знаю, inline-нотация не является валидной с точки зрения sphinx.
        В отличие от PyCharm, который делает статический анализ, PyFence делает проверки во время выполнения, безо всяких исключений.
    • 0
      Не очень сочетается с duck-typing. Ведь без PyFence достаточно было чтобы у объекта number был метод .as_integer_ratio() чтобы код работал, а с fence придётся обязательно наследоваться от float, иначе ругнётся.
      Конечно, на практике этим не часто пользуются, но идея именно в этом.
      • 0
        Пример — это изначально фиговый код, в котором должна была быть проверка типа аргумента (или хотя бы hasattr('as_integer_ratio')). Наследоваться от float — это пожалуй, еще хуже.
        • –1
          Пример — это нормальный код. Или вы серьёзно считаете, что «не-фиговый код» должен быть таким:
              def format(self, number):
                  if hasattr(number, 'as_integer_ratio'):
                      return '%i/%i' % number.as_integer_ratio()
                  else:
                      #?????
          

          docs.python.org/2/glossary.html#term-eafp

          В наследовании от float почему-то не вижу ничего плохого :-\
          • –1
            Я считаю что нормальный код должен начинаться с
            if type(number) is not float:
                raise ArgumentError(...)
            
            • +2
              Тогда Python вам противопоказан, ИМХО.
              • 0
                Скажите тогда то же самое разработчикам Django, например.
                ~/django ⭠ master $ ack-grep 'raise TypeError' . | wc -l
                157
                • 0
                  Ну, не так уж много на 76305 строк кода =)

                  Две трети из этого в contrib/gis почему-то.

                  Ладно, я всего лишь высказываю своё мнение. То что кто-то с ним не согласен меня устраивает.
                  • 0
                    Я же не предлагаю форсить типы аргументов повсеместно. Однако если функция неспособна работать с каким-либо типом, кроме определенного, будет человечно выдать программисту ValueError, а не заставлять лезть в ее потроха и выяснять, почему начинают сыпаться AttributeError/TypeError еще на пять фреймов ниже.
        • +1
          Можно не наследоваться от float, а добавить абстрактный класс с данным методом.
        • +3
          В Python 3.3.3 появилась очень мощная работа с интроспекцией и сигнатурой функций Function Signature Object, пример из PEP 362:

          @checktypes
          def test(a:int, b:str) -> int:
                return int(a * b)
          
          • +2
            Только вот декоратор checktypes не входит в стандартную библиотеку, увы. Его придется таскать везде с собой.
        • 0
          Я джва года такое для Objective C жду

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.