Архив рубрики: Python

pytest продолжение

В первой части статей мы немного познакомились с основными базовыми возможностями pytest. Рассмотрели его особенностей и подходы.

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

Последовательно выполнение тестовых сценариев


В тестировании часто бывают случаи, когда необходимо объединить несколько проверок в один большой тест и выполнить их последовательно. Причем бывает и так, что один сценарий зависит от результатов работы предыдущего (но такое лучше стараться избегать в автотестировании). 

В следующих статьях мы посмотрим как можно избежать этой ситуации пользуясь стандартными средствами пайтеста.

Например, тестируемая программа при первом запуске накатывает схему базы (sqlite чтобы не морочиться) и заполняет её некоторой служебной информацией. Допустим перед нами стоит задача «дешево» протестировать этот модуль. Сходу задачу модно разбить на 2 подзадачи — проверка создания файла базы, затем проверка схемы и в конце проверка данных.

Самый простой вариант — нумерация в названии тестов

# conftest.py
import os
import pytest
import sqlite3


def pytest_configure(config):

    config.DB_PATH = «db.sqlite»


@pytest.fixture(scope=»module»)
def creator(request):
    DB_PATH = request.config.DB_PATH
    con = sqlite3.connect(DB_PATH)
    cur = con.cursor()
    cur.executescript(«»»
        create table person(
            firstname,
            lastname,
            age
        );

        create table book(
            title,
            author,
            published
        );

        insert into book(title, author, published)
        values (
            'Dirk Gently''s Holistic Detective Agency',
            'Douglas Adams',
            1987
        );
        «»»)
    con.commit()
    con.close()
    return DB_PATH


@pytest.fixture
def cursor(request):
    class cursor:

        def __init__(self, dbpath):
            self.dbpath = dbpath
            self.conn = sqlite3.connect(self.dbpath)

        def table_exists(self, table):
            cursor = self.conn.cursor()
            cursor.execute(
                «SELECT name FROM sqlite_master WHERE type = «table»»)
            tables = cursor.fetchall()
            for each in tables:
                if table in each:
                    return True
            return False

        def getrows(self, table):
            cursor = self.conn.cursor()
            cursor.execute(«SELECT * FROM %s;» % (table))
            return cursor.fetchall()

    return cursor(request.config.DB_PATH)

# test.py
def test_1_db_exists(creator):
    assert os.path.exists(creator)


@pytest.mark.parametrize(«table», [«person», «book»])
def test_2_check_scheme(table, creator, cursor):
    result = cursor.table_exists(table)
    assert result


@pytest.mark.parametrize(«table», [«book»])
def test_3_check_rows(table, creator, cursor):
    assert cursor.getrows(table)


Спасибо документации питона за готовые примеры.

У такого подхода есть ряд недостатков.
1. В названии теста появляется нумерация — это лишняя мета информация. (Это может сказаться на тестовом отчете или билд логе CI сервера)
2. Название теста усложняется
3. Вставка нового элемента в середину приведет к смене нумерации всех последующих тестов

Вариант посложнее — маркеры


Свои маркеры мы реализовывать не будем, а используем готовый плагин pytest-ordering. В принципе вся реализация такого плагина составит не больше 100 строчек кода.

# test.py
@pytest.mark.order1
def test_db_exists(creator):
    assert os.path.exists(creator)


@pytest.mark.order2
@pytest.mark.parametrize(«table», [«person», «book»])
def test_check_scheme(table, creator, cursor):
    result = cursor.table_exists(table)
    assert result


@pytest.mark.order3
@pytest.mark.parametrize(«table», [«book»])
def test_check_rows(table, creator, cursor):
    assert cursor.getrows(table)


Наши тестовые функции преобразились, стали более понимаемы для чтения.
Из коробки плагин содержит создание цепочек выполнения, более понятные маркеры (pytest.mark.run('first'), pytest.mark.run('second')) и прочее. Более подробно в микро доку плагина.

Ссылки
[1] https://docs.python.org/2/library/sqlite3.html
[2] http://pytest-ordering.readthedocs.org/en/develop/

Автор: Евгений Курочкин

{} vs dict()


В питоне предусмотрена возможность создать словарь двумя способами — через фигурные скобки {} и через конструктор dict(). В чем разница и что лучше использовать в коде?


Код

a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}

Пример наглядно демонстрирует что при статическом определении атрибутов количество кода практически одинаковое (скобки больше всего лишь на 2 символа). 

Инициализация

Конструктор словаря предлагает расширенный функционал для инициализации словаря.

d = dict((str(v),v) for v in range(10))
dict(zip(['one', 'two', 'three'], [1, 2, 3]))


Скорость работы

Выполним в консоли следующие выражения

>>python -m timeit «{}»
10000000 loops, best of 3: 0.034 usec per loop

>>python -m timeit «dict()»

10000000 loops, best of 3: 0.127 usec per loop

Почему такая существенная разница в скорости? Все просто, смотрим что выдает диассемблер

>>import dis

>>def d(): t = {}

>>dis.dis(d)

  1           0 BUILD_MAP                0
              3 STORE_FAST               0 (t)
              6 LOAD_CONST               0 (None)

              9 RETURN_VALUE

>>def d(): t = dict()
>>dis.dis(d)

  1           0 LOAD_GLOBAL              0 (dict)

              3 CALL_FUNCTION            0
              6 STORE_FAST               0 (t)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE



При вызове конструктора dict() выполняется большее количество инструкций питона.

Вывод

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

PS. Все выводы актуальны и для списков


Ссылки

https://docs.python.org/2/library/stdtypes.html#mapping-types-dict
http://stackoverflow.com/questions/6610606/dict-literal-vs-dict-constructor-any-preferred

Автор: Евгений Курочкин

Топ 10 вопросов по python на stackoverflow

What does the yield keyword do in Python?
What is a metaclass in Python?
How to check whether a file exists using Python
Does Python have a ternary conditional operator?
Calling an external command in Python
How can I make a chain of function decorators in Python?
What does `if __name__ == “__main__”:` do?
How can I merge two Python dictionaries in a single expression?
Sort a Python dictionary by value
How do I install pip on Windows?

Довольно ожидаемо

[1] stackoverflow.com

Автор: Евгений Курочкин

Основы операции импортирования пакетов

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

Так как же выполняется импортирование пакетов? В инструкциях import, там, где вы указывали имя простого файла, можно указать список имен в пути к каталогу, разделяя их символами точки:
import dir1.dir2.mod
То же самое относится и к инструкции from:
from dir1.dir2.mod import x
Предполагается, что такой «точечный» путь в этих инструкциях соответствует пути через иерархию каталогов на вашей машине, ведущему к файлу mod.py (или к файлу с похожим именем – расширение в имени файла может быть другим). Таким образом, предыдущие инструкции указывают, что на вашей машине имеется каталог dir1, в котором существует подкаталог dir2, в котором находится файл модуля mod.py (или с похожим именем).
Кроме того, эти инструкции предполагают, что каталог dir1 находится внутри некоторого контейнерного  каталога  dir0,  который  находится  в  пути  поиска модулей. Другими словами, обе инструкции импорта предполагают наличие структуры  каталогов,  которая  выглядит  примерно  так,  как  показано  ниже (здесь в качестве разделителей имен каталогов используется символ обратного слеша, принятый в операционной системе DOS): dir0dir1dir2mod.py  # Или mod.pyc, mod.so и так далее
Контейнерный каталог dir0 должен быть добавлен в путь поиска модулей (если это не домашний каталог главного файла программы), как если бы имя dir1 было именем модуля. Инструкция import в вашем сценарии определяет пути, ведущие непосредственно к модулям, начиная от этого каталога.
В  любом  случае  самый  левый  компонент  пути  в  операции  импортирования пакета вычисляется относительно некоторого каталога, включенного в путь поиска модулей, – в список sys.path
То есть инструкции import в вашем сценарии должны содержать полный путь 
к импортируемым модулям относительно этого каталога.

Автор: Няшный Человек
Дата публикации: 2015-12-15T22:28:00.000+02:00

MagicPython — Syntax Highlighter для SublimeText

Мой приятель Юра Селиванов попросил написать рекламный пост о его новом проекте MagicPython.

Это syntax highlighter для Sublime Text и Atom, который поддерживает все новые языковые конструкции Python 3.5 (async def и await например) плюс type annotations, string formatting и регулярные выражения.

Sublime поддерживает Python из коробки, но с Python 3 (а особенно с Python 3.5) у него проблемы. MagicPython понимает всё.
Разметка шаблонов для форматирования строк и регулярок заслуживает отдельного упоминания — выглядит прекрасно и заметно облегчает жизнь.

Я сам использую ортодоксальный Emacs и переходить (пока) не собираюсь, но MagicPython работает исключительно хорошо и заслуживает доброго слова.

К слову, Юра (https://twitter.com/1st1) Python Core Developer и автор PEP 492 aka async/await — т.е. очень грамотный спец, который знает как делать качественные продукты.

Пользуйтесь с удовольствием.

P.S.

О редакторе Atom узнал только на этой неделе.
На первый взгляд выглядит как Sublime но при этом Open Source.
Уважаемые читатели, кто-нибудь его использует? Какие ваши впечатления?

Автор: Andrew Svetlov