понедельник, 12 мая 2014 г.

LevelUp ищет питонщиков

Компания Level Up, где я имею честь работать, набирает питонщиков.

Уровень кандидатов может быть разный: мы ищем талантливых людей.

Особенно интересуют позиции senior software engineer и team/tech leader.

Задачки интересные.

Технологии разные:

  • стандартный WSGI (я предпочитаю Pyramid, много где используется Django)
  • асинхронное программирование на Tornado и Twisted
  • базы данных — самые разные: redis, PostgreSQL, MySQL/MariaDB, mongo. Осторожно пробуем Cassandra.
  • для новых разработок применяем Python 3 и asyncio
  • есть задачи для спецов по data mining и анализу текстов

Зоопарк технологий объясняется тем, что компания работает над кучей взаимосвязанных сервисов, которые общаются между собой через REST API. При этом сервисы могут быть написаны на разных платформах (Java, PHP, node.js) и тут уже протокол взаимодействия гораздо важнее конктретных библиотек.

Бизнес построен не на аутсорсинге.

Мы — продуктовая компания

Т.е. мы создаём инструменты, которые сами же используем. Это очень здорово и удобно, когда постановщик бизнес-требований сидит в соседней комнате.

И вместе с тем у нас нет ужасного монолитного кода, написанного во времена когда по Земле бродили мамонты, — и который должен поддерживаться во что бы то ни стало потому что тестов нет, а менять без тестов страшно.

Работа в офисе на Подоле (Киев).

Традиционный полный рабочий день, зефир в холодильнике и абонементы на спортзал/бассен, всё такое прочее.

Если вакансия заинтересовала — пишите на andrey.svetlov@levelupers.com

среда, 7 мая 2014 г.

aiopg и SQLAlchemy

Выпустил новую версию aiopg 0.2 -- библиотеки для работы с PostgreSQL из asyncio.

aiopg использует асинхронные вызовы и в этом похож на txpostgres и momoko -- библиотеки для работы с PostgreSQL под twisted и tornado соответственно.

В новой версии aiopg появилась опциональная поддержка SQLAlchemy Core Expressions.

Проще один раз показать.

Создаем описание структуры базы данных:

import sqlalchemy as sa

metadata = sa.MetaData()

users = sa.Table('users', metadata,
                 sa.Column('id', sa.Integer, primary_key=True),
                 sa.Column('name', sa.String(255)),
                 sa.Column('birthday', sa.DateTime))

emails = sa.Table('emails', metadata,
                  sa.Column('id', sa.Integer, primary_key=True),
                  sa.Column('user_id', None, sa.ForeignKey('users.id')),
                  sa.Column('email', sa.String(255), nullable=False),
                  sa.Column('private', sa.Boolean, nullable=False))

Как видите -- две таблицы, связанные отношением один-ко-многим. Для тех, кто не знаком -- алхимия позволяет описать любую модель данных, которая только может прийти в голову. Индексы, constraints, пользовательские типы данных такие как array и hstore -- тоже.

Теперь нужно сделать engine:

from aiopg.sa import create_engine

engine = yield from create_engine(user='aiopg',
                                  database='aiopg',
                                  host='127.0.0.1',
                                  password='passwd')

engine содержит внутри connection pool.

Для работы с БД нужно получить connection и что-нибудь выполнить:

with (yield from engine) as conn:
    uid = yield from conn.scalar(
        users.insert().values(name='Andrew', birthday=datetime(1978, 12, 9)))

Обратите внимание: диалект знает о INSERT ... RETURNING и позвращает primary key для вставляемой записи.

Работа с транзакциями:

with (yield from engine) as conn:
    tr = yield from conn.begin()

    # Do something

    yield from tr.commit()

Получение данных:

with (yield from engine) as conn:
    res = yield from conn.execute(users.select())
    for row in res:
        print(res)

Сложный запрос:

with (yield from engine) as conn:
    join = sa.join(emails, users, users.c.id == emails.c.user_id)
    query = (sa.select([users.c.name])
             .select_from(join)
             .where(emails.c.private == 0)
             .group_by(users.c.name)
             .having(sa.func.count(emails.c.private) > 0))

    print("Users with public emails:")
    ret = yield from conn.execute(query)
    for row in ret:
        print(row.name)

Вызов SQL функций:

with (yield from engine) as conn:
    query = (sa.select([sa.func.avg(sa.func.age(users.c.birthday))])
             .select_from(users))
    ave = (yield from conn.scalar(query))
    print("Average age of population is", ave,
          "or ~", int(ave.days / 365), "years")

sa.func.avg и sa.func.age выполняются на стороне SQL сервера.

Полный код примера здесь, документация здесь.

суббота, 3 мая 2014 г.

Работаем с pip

Думаю, все применяют pip и знают основы:

$ pip install -U sqlalchemy
$ pip install -r requirements.txt
$ pip freeze > requirements.txt
$ pip uninstall sqlalchemy

Давайте посмотрим, что ещё полезного умеет эта команда

Ставим пакет локально для пользователя

$ pip install --user pep8

Т.е. если мы не в виртуальном окружении (virtualenv/virtualenvwrapper), то пакет pep8 будет установлен куда-то вроде ~/.local/lib/python3.4/lib.

Главная прелесть метода -- не нужны права суперпользователя для установки пакетов (не надо писать sudo pip install ..., К тому же так гораздо аккуратней.

Создаем конфигурационный файл

Т.к. опция --user нужна часто, стоит записать её в конфиге как значение по умолчанию. Создаем файл ~/.pip/pip.conf и пишем в него

[install]
user = true

Готово.

Внимание: user=true не работает с виртуальными окружениями.

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

Реальный пример: я поставил pyzmq. Библиотека скачалась и скомпилировалась. Потом я заметил что она скомпилировалась со неправильной версией libzmq. libzmq я переставил, теперь нужно пересобрать pyzmq:

$ pip install -U --force-reinstall pyzmq

Для работы --force-reinstall обязательно нужно указывать -U (--upgrade).

Смотрим на список установленных (доступных) библиотек

$ pip list

Выясняем, какие библиотеки можно обновить

$ pip list -o
$ pip list --outstanding

Для выполнения команды pip неоднократно делает зазыр в интернет, так что придется подождать.

Устанавливаем библиотеку для работы над ней

Находясь в корне проекта, в той же папке где лежит setup.py:

$ pip install -e .

Этот способ лучше вызова

python setup.py develop

хотя бы тем что работает даже если setup.py не использует setuptools.

Установка необязательных зависимостей

Если setup.py содержит extras_require то дополнительные зависимости можно установить так:

$ pip install -e .[PDF,reST]

Получаем информацию об установленной библиотеке

$ pip show pyflakes

Так можно узнать версию, местонахождение на диске и зависимости.

Ищем в PyPI

msgpack имеет неудобное имя библиотеки (msgpack-python), которое я постоянно забываю.

$ pip search msgpack

покажет список всех пакетов, в названии которых есть msgpack.

Только не делайте:

$ pip search django

Дуплит пару минут, выдает 5754 результата :)

Автодополнение

И, наконец, последнее. Я люблю пользоваться bash completion. Чтобы настроить эту удобную штучку для pip выполните:

$ pip completion --bash >> ~/.bashrc

или для zsh:

$ pip completion --zsh >> ~/.zprofile

Как ни странно pip --help стесняется рассказать о том, что у него есть команда pip completion -- но оно работает.