In [9]:
%%html
<style>
.text_cell_render * {
   font-family: OfficinaSansCTT;
}
.reveal code {
    font-family: OfficinaSansCTT;
}
.text_cell_render h3 {
   font-family: OfficinaSansCTT;
}
.reveal section img {
    max-height: 500px;
    margin-left: auto;
    margin-right: auto;
}
</style>


Вопросы

  • Функция parser.parse_known_args() возвращает два значения. Что в них находится?
  • Какие два основных типа собранных пакетов существуют в Python?
  • Где Python ищет модуль, когда мы пишем "import foobar"?
  • Зачем нужен MANIFEST.in и как включить его поддержку?
  • Зачем нужен .gitignore?
  • Каким образом мы строим дерево разделов в Sphinx?

Логирование

  • Можно кидать лог в одно или несколько мест одновременно
  • Разные уровни ошибок:
    • DEBUG
    • INFO
    • WARNING
    • ERROR
    • CRITICAL
  • Расширенные возможности форматирования
  • http://docs.python-guide.org/en/latest/writing/logging/

In [ ]:
import logging
import sys

logger = logging.getLogger(__file__)  # логгер идентифицируется по имени
logger.setLevel(logging.DEBUG)   # глобальный уровень логирования (WARNING по умолчанию)
fh = logging.FileHandler('test.log')  # обработчик для записи в файл, еще есть RotationFileHandler
fh.setLevel(logging.DEBUG)  # выставляем уровень логирования конкретного обработчика
ch = logging.StreamHandler()  # обработчик для записи в stderr (по умолчанию)
ch.setLevel(logging.ERROR)  # логируем сообщения ERROR и CRITICAL
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)  # форматируем
fh.setFormatter(formatter)  # можно назначить разное форматирование для разных обработчиков
ch.setFormatter(formatter)

logger.addHandler(fh)  # добавляем обработчики
logger.addHandler(ch)

In [ ]:
logger.info("We're in a jungle")
logger.warning("Oh no! It's a snake!")
logger.error("Nowhere to run!")
logger.critical("It bit be!")
logger.debug("Ha, it's just a small Python")

"Сконфигурить сразу все"

Тестирование


In [ ]:
# test_code.py

import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

setUp() и tearDown()

  • Запустить код перед тестом и после него

Упражнение

Сгенерируйте файл 'input.txt' из 100 случайных чисел от -1000 до 1000 (по одному на строчке). Напишите программу, которая читает этот файл и находит 10 самых больших чисел в файле, не используя sorted() (дополнительные очки за использование, например, модуля heapq). Постарайтесь учесть ошибки (файл отсутствует, в файле не числа, файл пуст и т.п.). Придумайте, как структурировать программу таким образом, чтобы ее было легко тестировать, и напишите в соседнем файле тест для нее, желательно не менее четырех методов в кейсе.

Динамическая подмена объектов - мок

  • https://docs.python.org/3/library/unittest.mock.html
  • Для того, чтобы что-то функционально протестировать - надо это изолировать
  • Особенно внешние вызовы - запросы к сайтам, работу с базой и т.д.

Предположим, у нас такой вот код


In [ ]:
# my_code.py

def read_file(fname):
    with open(fname, "r") as f:
        for line in f:
            yield line

def do_cool_stuff(filename):
    s = 0
    for line in read_file(filename):
        s += int(line.strip())
    return s

In [ ]:
# test_my_code.py

import unittest
from unittest.mock import MagicMock

import my_code


class TestMyCode(unittest.TestCase):

    def test_cool_stuff(self):
        my_code.read_file = MagicMock()
        my_code.read_file.return_value = iter([1, 2, 3])
        self.assertEqual(
            my_code.do_cool_stuff("fakefile"),
            [1, 2, 3]
        )
        

if __name__ == '__main__':
    unittest.main()

In [ ]:
import pytest
from pytest_mock import mocker

import my_code

def test_load_list_extended(mocker):
    my_code.read_file = mocker.MagicMock()
    my_code.read_file.return_value = iter(['a', 'b', 'c'])
    expected = ['a', 'b', 'c']
    assert my_code.do_cool_stuf('some_file') == expected