This test is too slow

Published 7/28/2017 by e0ne in Python

Sometimes we need to understand why unit-test is so slow. Sometimes I’m to lazy to go deep to understand why.

That’s why I’ve created a very simple profiled class to make unit-tests profiling fast and simple. I used only cProfile, so it will work on any Python project. It’s so simple, so I can’t talk about it more. You can install it via ‘pip install ProfiledTest’ and use it like:

class SampleTest(ProfiledTest, unittest.TestCase):
    def test_sample(self):
        self.assertTrue(True)

 

GitHub url: https://github.com/e0ne/profiled_test


Проблема пришла от туда, от куда не ждали. А именно от таких 5-ти строчек кода:

class CustomModel(Base):
    __tablename__ = 'custom_model'
    id = Column(Integer,)
    name = Unicode(100)
    datetime = Column(DateTime, default=datetime.now())

Когда-то давно я или неправильно понял доку, недочитал или прочитал не то, но был уверен в том, что этот код “компилируется” в примерно такой SQL (код приведен только в целях примера и может не работать:) ):

CREATE TABLE custom_model (
     id integer,
     name varchar(20),
    created_at datetime default getdate().now
)

Проблема проявилась в том, что order by по полю created_at не работал. Все дело в том, что вышеприведенный код генерируется в такой SQL:

CREATE TABLE test (
     id integer,
     name varchar(20),
     created_at
)

Т.е. значение по умолчанию выставляются не на уровно СУБД, а на уровне модели при вставке. Более того, код “default=datetime.now()” отрабатывает только раз при создании модели и все значения будут одинаковыми. Для исправления этого достаточно сделать что бы значение по умолчанию вычислялось каждый раз новое. Для этого в параметр default можно передать любой callable объект, например lambda-функцию (default=lambda: datetime.now()). Если копнуть дальше в исходники, что значение default - это инстанс типа ColumnDefault, спрятанный за удобным синтаксисмом.

Что бы значения по умолчанию выставлялись на уровне СУБД в SQLAlchemy необходимо использовать параметр server_default (к слову, onupdate и другие триггеры на сервере ставятся с помощью добавления префикса “server_”, например server_onupdate). Если необходимо в качестве значения по умолчанию задать какое-то выражение (sql expression), то делается это с помощью следующей конструкции:

server_default=text("sysdate")

К слову о  Django - там все работает так же, только про server_default я ничего не нашел:(.

Ссылки по теме:

 

 



Если коротко, то типы в Python делятся либо на встроенные и пользовательские, либо на mutable и immutable (сразу так и не подобрал подходящего перевода на русский язык). Ну а если немного подробнее, то как-то так:

Рассмотрим простой пример кода (здесь и далее примеры кода будут писаться и выполняться в ipython’е):

In [1]: a = 1000
In [2]: a
Out[2]: 1000
In [3]: type(a)
Out[3]: int

Здесь мы объявляем переменую создаем объект (object) a, со значением(value) 1000 типа(type) int. Объекты в python - это абстрация над данными. Поэтому любой объект содержит в себе идентификатор(identity), тип и значение. Идентификатор - это адрес объекта в памяти, который никогда не меняется. Но мы можем присвоить объекту новое значение, написав, следующий код:

In [4]: a = 'hello'
In [5]: a
Out[5]: 'hello'
In [6]: type(a)
Out[6]: str

Теперь мы присвоили объекту a новое значение типа string. За кулисами этого всего, интерпритатор сделал, примерно, такое: создал новый объект ‘hello’, и поменялл ссылку переменной a на новый объект в памяти. При этом старое значение 1000 все еще хранится в памяти и будет удалено сборщиком мусора, т.к. на него нет ни одной ссылки. Тоже самое происходит и присвоении переменной нового значений такого же типа:

In [7]: b = 1000
In [8]: id(b)
Out[8]: 40235856
In [9]: b = 1001
In [10]: id(b)
Out[10]: 39189680

Интересный эффект будет при использовании целых чисел в диапазоне от -5, до 256 - их id всегда будет одинаковый, т.к. для улучшения быстродействия интерпритатор при старте создает объекты с этими значениями.

Стандартные типы в CPython делятся на два типа: mutable и immutable. Mutable — это обекты, значения которых могут быть изменены(например, list), а immutable — это обекты, значения которых не может меняться (например, string). Но это не значит, что если у нас есть переменная immutable типа, то ее нельзя изменить. Например, с типом string все привыкли работать так:

In [11]: text = 'Hello'
In [12]: text
Out[12]: 'Hello'
In [13]: text = 'Hello, World'
In [14]: text
Out[14]: 'Hello, World'

Как мы видим, начение переменной text поменялось. На самом деле, при присвоении переменной text нового значения, в памяти создается новый объект, и переменная text начинает на него ссылаться. В этом легко убедится при помощи функции id - получить идентификатор до и после изменения переменной.

Mutable типы ведут себя так:
In [15]: l = [1, 2, 3]
In [16]: l
Out[16]: [1, 2, 3]
In [17]: id(l)
Out[17]: 139901299808088
In [18]: l.append(4)
In [19]: l
Out[19]: [1, 2, 3, 4]
In [20]: id(l)
Out[20]: 139901299808088

Основываясь на этом свойстве(и не только), ключами в словаре (dict) могут быть только immutable обекты:

In [21]: d = {'one': 1}
In [22]: type(d)
Out[22]: dict
In [23]: d[1]=1
In [24]: d
Out[24]: {1: 1, 'one': 1}
In [25]: d[l] = 'list'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-4f49b18d7af7> in <module>() ----> 1 d[l] = 'list'
TypeError: unhashable type: 'list'

Immutable типы в Python — это числа(numbers), строки (strings) и кортежи (tuples).

Для облегчения привжу список некоторых всторенных типов (built-in types):

 

  • Numbers (числа) — int, long, float, complex(3+4j), Decimal, Fraction
  • Sequence Types (последовательности) — str, unicode, list, tuple, bytearray, buffer, xrange
  • Sets — set, frozenset
  • Maps (хеш-таблицы) — dict
  • Files, context managers и другие.

 

Ссылки по теме:

 

 

 


Скорее заметка для себя. Постоянно забываю об этом ключе.

Иногда нужно очень быстро поднять HTTP-сервер с минимальным функционалом. Например, нужно проверить HTML+JavaScript, который работает только через веб-сервер. Я, конечно понимаю, что у меня под рукой всегда есть, как минимум nginx, и поднять на нем сайт, который будет раздавать статические фалы, делов на 5-10 минут. Но, во-первых, это долго, а во-вторых, этот самый nginx у меня находится на виртуалке, пусть и включенной в 90% времени на ноутбуке. А тут прийдется не только веб-сервер настроить, но и расшаренные папки в VirtualBox-е, и так далее...

Единственное, что у меня действительно всегда есть под рукой, это Python (уже несколько лет не пользуюсь ОС Windows, а на всех *nix он есть из коробки). Поднять простой тестовый веб-сервер на питоне можно всего одной командой:

$ python -m SimpleHTTPServer 8100

После этой команды, у вас локально запустится веб-сервер на 8100-м порту, который покажет содержимое текущей папки.

Работает это благодаря ключу -m, который указывает интерпритатору о том, что необходимо выполнить модуль как скрипт. Это идентично команде "$ python /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SimpleHTTPServer.py", но писать значительно проще. Единственное ограничение, что этот модуль должен быть доступен в sys.path


Давно решил не писать отзывы к библиотекам/фреймворкам, но эта поражает меня уже второй раз так, что в твиттере не помещается вся мысль.

TestFixtures (http://packages.python.org/testfixtures/) - приятное дополнение, а в некоторых случаях, и замена Mock.

То, что она умеет делать mock’и объектов - этим никого не удивишь. Вся прелесть TestFixtures в том, что в ней уже из коробки доступны те самые вещи, которые часто приходится писать самому, тем самым изобретать свой велосипед:(.

Начиная от небольших функций, вроде generator и wrap, библиотека включает в себя то, из-за чего лично я ее использую: различные helper’ы для тестирования логгирования и вывода в потоки (stream’s) (когда-то очень помогла, найти ошибку и некорректным перехватом исключения и, вследствии чего, потерей части логов), а также всякие полезности для тестирования работы с датами и исключеними. 

Для меня это темерь однозначно must use в юнит-тестах.


4 января вышел релиз кандидат fullstack-фрейморка для разработки веб-приложения Django. Обзоры, наверено, не писали/читали только ленивые. Но пишут, в основном, про мажорные фичи, из-за которых и выпускают релиз. Я перевел свой небольшой прототипчик одного приложения на Django 1.5 RC и поюзал некоторые минорные нововведения, о которых пишут мало, но которые почти делают каждый релиз тем, из-за чего часто хочется использовать именно его. Из того, что мне понравилось - это:

  • изменения в template engine: теперь True, False, None воспринимаются так же, как и в python;
  • дополнительные батарейки для работы с временными зонами - мелочь, а очень приятно;
  • исправленна ошибка OutOfMemory при использовании команды dumpdata - особенно полезно на небольших хостингах;
  • mod_wsgi auth handler - для тех, кто все еще использует Apache и Basic авторизацию;
  • в debug конфигурации приложения логи дополнительно выводятся в консоль;
  • user_login_failed событие - понятно что это такое, +1 к секьюрити: легче блокировать ботов от перебора паролей и плюс к защите от DDoS;
  • loaddata имеет опцию для игнорирования колонок, которых больше нет в модели - просто в восторге от этой фичи, имхо, она для меня теперь станет неаменимой при разработке, кода модель активно меняется, а django south использовать еще рано (в момент разработки, а не при выходе в production).
Из всего вышесказанного, кроме mod_wsgi auth handler попробовал все и хоче сказать: дявол кроется в деталях, они делают любой продукт именно таким, чтоб им (не) хотелось пользоваться.

Ну и не могу сказать про одно мажорное изменения - эксперементальная поддержка Python 3.2+! Осталось подождать и/или портировать нужные зависимости для проектов и можно начинать использовать. На свой страх и риск, конечно, т.к. production код должен быть как можно стабильнее, а не в статусе "эксперементальная фича". Хотя, time to market никто не отменял...

Подробности на оффициальном сайте Django:


В этор раз идет подготовка сразу к двум KhakrivPy, соответственно докладчиков нужно найти больше.

26-го января пройдет KharkivPy #0, посвященный функциональным языкам программирования (Erlang, Lisp, Haskel, Scala, etc). На данный момент к нам в гости с докладом согласился приехать из Киева Владимир Кирилов (http://kirillov.im/, @darkproger) с докладом про Erlang, тема уточняется.

Ищем докладчиков, которые расскажут что-то интересное о других языках и/или про Erlang. Опыт в продкашн приветствуется.

2-го марта традиционный KharkivPy #7, на который тоже ищутся докладчики.

Желающие поделиться своим опытом могут обращаться ко мне  по телефону(+380919050626), скайпу (e0ne-user) или email'у (e0ne@e0ne.info)ю

Так де 19-го января в Киеве пройдет KyivPy, туда так же требуются докладчики. Подробности тут.

 

P.S. Этот пост написан в рамках акции, в которой я принимаю участие, которая запущена Стефани Бус и называется #back2blog, подразумевает написание 10 постов в блог за 10 дней. Это 1й пост из 10.


Lettuce и Python3

Published 12/3/2012 by e0ne in Python

 

Решил я для своих маленьких и уютных домашних проектов (pet project'ов) использовать Python 3.3. Казалось бы, ничто не предвещало беды. Ну кроме как отсутстие поддержки Python 3.x у некоторых библиотек. В частности, Lettuce(http://lettuce.it/).

Но так, как я уже выбрал не самый простой, на данный момент, путь (да, я про python3), то отступать было не куда, решил портировать Lettuce под Python 3.3. Возможно, свою роль в этом сыграли еще свежие воспоминания о UA Pycon 2012, в частности, доклад Михаила Коробова “Как всем перейти на Python 3.x” (http://ua.pycon.org/talks/26).

Дальше все понеслось и после нескольких часов ковыряния в исходниках Lettuce и его зависимостей, github fork, py2to3, http://wiki.python.org/moin/PortingPythonToPy3k, http://lettuce.it/dev/index.html, https://groups.google.com/forum/?fromgroups=#!topic/lettuce-developers/MaOPzOuMQzg и постоянных попытках запустить это все получилась первая рабочая версия:), commit, push, кофе, кофе, печеньки... До беты, конечно, еще писать и писать (фиксить и фиксить), но начало уже есть  и отступать некуда, ибо сзади остались только Python 2.x и большой enterprise.

Ссылки на GitHub repos:

 

 

Продолжение следует...

 


Переодически сталкиваюсь с этой проблемой и приходится гуглить. Решил, записать, что бы проще искать.

Собственно, проблема выглядит так:

File "/home/e0ne/src/project/.venv/app/lib/python2.7/locale.py", line 496, in getdefaultlocale
return _parse_localename(localename)
File "/home/e0ne/src/project/.venv/app/lib/python2.7/locale.py", line 428, in _parse_localename
raise ValueError, 'unknown locale: %s' % localename
ValueError: unknown locale: UTF-8

Проблема заключается в том, что для текущего сеанса шелла(bash, etc) не настроена системная локаль. Ошибка позникала как под Linux(Ubuntu, RHEL-based), так под Mac OS. Фиксится просто:

Добавляем в ~/.bashrc следующие строки:

export LANG="en_US.UTF-8"
export LC_COLLATE="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
export LC_MESSAGES="en_US.UTF-8"
export LC_MONETARY="en_US.UTF-8"
export LC_NUMERIC="en_US.UTF-8"
export LC_TIME="en_US.UTF-8"
export LC_ALL=

Вместо "en_US" нужно(можно) подставить нужное значение. Таже, можно выполнить эти строки в шелле и это будет работать до конца сеанса.


 

Из wiki.openstack.org:

Quantum is an OpenStack project to provide "network connectivity as a service" between interface devices (e.g., vNICs) managed by other Openstack services (e.g., nova).

Из описания можно предположить, что в будущем это станет заменой nova network, что не далеко от правды. Ниже я расскажу об установки OpenStack + Quantum и немного о самом Quantum.

Установка OpenStack c помощью скриптов devstack является одной из самых простых и быстрых. В простейшем случае, это выглядит так:

$ git clone https://github.com/openstack-dev/devstack.git
$ cd devstack && ./stack.sh

После этого нужно будет лишь ввести свой root-пароль, пароль к MySql серверу и пароли к Openstack’у. При установке Quantum нужно создать и/или отредактировать файл localrc в каталоге с Devstack’ом и добавить туда следующие строчки:

disable_service n-net
enable_service q-svc
enable_service q-agt
enable_service q-dhcp
enable_service quantum
LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
Q_PLUGIN=openvswitch

Разберем эти строчуки подробнее:

 

  • LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver - настраивваем libvirt для корректной работы файрволла с Quantum
  • Q_PLUGIN=openvswitch - указываем, что в качестве плагина (back-end’а) использовать Open vSwitch.
  • disable_service n-net - отключение nova-network, теперь вместо этого компонента будет работать Quantum.
  • enable_service q-svc - включаем Quantum Server. По сути, после скачивания исходников и первоначальной настройки выполнится такая команда: “/opt/stack/quantum/bin/quantum-server --config-file /etc/quantum/quantum.conf --config-file /etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini”. На момент написания этого поста есть баг, в котором описано что Quantum plugins должны использовать тот жу конфигурационный файл, что и Quantum. Но пока это не пофикшено.
  • enable_service q-agt - запустить Quantum agent. Т.к. в качестве плагина был выбран Open vSwitch, то и запустится, соответственно, Open vSwitch Plugin: $ sudo python /opt/stack/quantum/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py --config-file /etc/quantum/quantum.conf --config-file /etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
  • enable_service q-dhcp - Quantum DHCP Agent, который управляет DHCP сервером внутри нашей виртуальной сети. Запускается он следующей командой:  sudo python /opt/stack/quantum/bin/quantum-dhcp-agent --config-file /etc/quantum/quantum.conf --config-file=/etc/quantum/dhcp_agent.ini.  Из коробки это dnsmasq, но можно написать поддержку любого сервера.

 

После правки localrc можно запускать stack.sh и подождать пока все будет установленно.

Кроме установки самого OpenStack’а, devstack так же создает тестовых пользователей, проектов и сети. Т.к. говорим о Quantum, то о сетях подробнее.

Сам по себе Quantum (так сказать, его core) предоставляет только API для создания и управления сетями. Самим управлением занимаются его плагины (plugins), которые, imho, правильнее было бы назвать бэк-ендами (back-ends). В текущей версии (Folsom) возможна одновременная работа только одного плагина. Т.к. мы установили Open vSwitch, то далее буду описывать работу Quantum с ним.

Основные понятия в Quantum:

 

  • network - изолированный L2 сегмент сети, аналог VLAN;
  • subnet - блок IPv4 или IPv6 адресов и их конфигурация (маршрутизатор, DNS-сервер)
  • port - точка подключения устройств (vNICs) в сеть Quantum.

 

Если смотреть на взаимодействие Quantum Network и OpenStack, то это выглядит так:

У каждого тенанта может быть одна и более сетей. У каждой сети может быть от 1 до n подсетей (конечно, может быть и 0 сетей/подсетей, по смысла в этом нет). Если смотреть на то, как все рабтало без Quantum, то теперьешине subnets это аналоги nova networks. При замуске виртуалки (инстанса), ей нужног передать к какой из сетей тенанта она подключена. Для каждой подсети будет создан отдельный vNIC у инстанса, который будет подключен к какому-либо порту. В нашем случае - это порт в Open vSwitch.

Посмотрим как это работает на практики:

Open vSwitch работает поверх бриджей (bridge), поэтому посмотрим какие “мосты” у нас есть:

$ sudo ovs-vsctl list-br
br-int
br-tun

Сейчас нас интересует br-int, поэтому посмотрим какие у него есть порты:

$ sudo ovs-vsctl list-ports br-int

patch-tun
tap6d98326d-5a
tap6e1a8612-27
tapf842abe0-27

В данной конфигурации, у меня запущено два инстанса, которые подключены к портам tap6e1a8612-27 и tapf842abe0-27. Порт patch-tun служит для “проброса” трафика между виртуальной сетью и физической через TUN-интерфейс. К порту tap6d98326d-5a подключен DHCP-сервер(dnsmasq) для инстансов внутри subnet.

Посмотрим, какие у нас есть сети в Quantum:

$ quantum net-list

 

И подсети:

$ quantum subnet-list

 

Порты:

$ quantum port-list

 

Тут мы видем, что у нашего DHCP-сервера адрес 10.0.1.2, а у инстансов 10.0.1.3 и 10.0.1.4.

 

Теперь, когда мы хоть немного имеем представление о сети, попробуем запустить новый инстанс:

$ nova boot --flavor 1 --image 4032fc9c-4688-4e71-ba2d-5a90e7698230 --nic net-id=3d5a9b8d-40cf-4f7b-a344-3db20f1a4783 test_vm_3

Синтаксис стандартный за исключением того, что я явно передал к какой сети подключать инстанс “--nic net-id=3d5a9b8d-40cf-4f7b-a344-3db20f1a4783”

Убедимся, что инстанс запустился командой “nova list”. И если у него статус ACTIVE, то можно попробовать попинговать или зайти по SSH:

Тут мы обнаруживае, что из хоста у нас нет доступа к виртуалкам :(. Хотя, если зайти через VNC на виртуалки, то они нормально будут видет другие инстансы в той же подсети.

 

Все дело в том, что Quantum организует работу с сетями через network netspace (http://stuff.onse.fi/man?program=ip-netns&section=8), поэтому, алгоритм работы немного усложняется:

смотрим список текущих неймспейсов:

$ ip netns

Выполняем ping/ssh внутри нужного неймспейса:

~$ sudo ip netns exec qdhcp-3d5a9b8d-40cf-4f7b-a344-3db20f1a4783 ssh 10.0.1.4

 

Ссылки по теме:

 

Продолжение следует...