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

Документация South — Перевод. Что такое миграция?

Для непосвящённых: миграция (migrations), так же известная как 'schema evolution' или 'mutations' — это способ изменения схемы вашей БД с одной версии на другую. Django сам по себе может лишь добавлять новые модели, но почти во всех проектах часто происходят изменения самой модели — добавляются поля, изменятся свойства полей, и т.д.
South, и другие аналогичные решения, предоставляет способ обойти эту проблему, давая Вам инструмент для лёгкого и предсказуемого изменения схемы вашей БД. Вы пишете миграцию, которая говорит South как перейти с одной версии на другую, и, связывая эти миграции в цепочку, Вы можете двигаться вперёд или назад по истории изменений схемы вашей БД. 
В South, миграция это так же способ создания БД — первая миграция на самом деле миграция из пустой схемы к вашей схеме. Таким образом Вы можете как воссоздать самую последнюю версию схемы БД, так и проапгрейдить любую из устаревших, просто запустив миграцию с определённой точки. 
В процессе этого руководства мы покажем Вам как миграция работает и как она может быть полезна на разных примерах.

Автор: Ishayahu Lastov

Документация South — Перевод. Про South

South привносит миграцию в приложения на Django. Его главная цель — предоставить простой, стабильный и независящий от БД слой миграции чтобы избавить Вас от хлопот по обслуживанию изменений схемы БД в ваших приложениях.

Мы стараемся сделать South как простым в использовании, так и интуитивно понятным, насколько это возможно. Для этого мы автоматизируем большую часть задач по изменению схемы в то же время предоставляя мощный набор инструментов для сложных проектов. Вы можете легко написать вашу собственную миграцию руками или использовать API для изменения БД.

Хотя South начинался как малоизвестный проект, он медленно набирал популярность и теперь стал одним из самых популярных инструментов для изменения схемы приложений Django.

Основные возможности

  • Создание автоматической миграции: South может отслеживать изменения в файле models.py file и автоматически создавать миграцию, которая отражает эти изменения.
  • Не зависит от конкретной БД: Насколько это вообще возможно South не зависит от используемой Вами БД, поддерживая 5 типов БД.
  • Подкован в приложениях: South знает что такое приложения и умеет с ними работать, позволяя Вам проводить миграцию для некоторых ваших приложений и оставляя остальные на совести syncdb.
  • VCS-proof: South оповестит Вас если кто-то ещё делает миграцию для выбранного приложения и ваши миграции вступают в конфликт.

Краткая история

Изначально South был разработан Torchbox в 2008, когда ещё не было другой системы, которая могла предоставить нам необходимый функционал. В короткие сроки код системы был открыт и она получила популярность после представления на панели Schema Evolution на DjangoCon 2008.

Где-то в 2009, она стала самой популярной системой среди всех альтернативных вариантов и, похоже, её популярность будет только расти. Хотя есть множество запросов по интеграции South или чего-то похожего напрямую в Django, такой интеграции ещё не было сделано, в основном по причине некоторой незрелости систем миграции.

Автор: Ishayahu Lastov

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

В этом посте я писал почему работа с файлами и другими объектами, требующими гарантированного закрытия должна должна производиться через with. Однако кроме минуса в виде добавления в код лишнего уровеня вложенности with еще и решает только часть проблемы — если код обработки файла не локален (нужно возвращать дескриптор в вызывающий код или хранить неопределенное время) with не может помочь. И собственно никто вообще не может помочь — суровая реальность состоит в том, что python не гарантирует вызов деструктора объекта. Т.е. если вы работаете на CPython, и не создаете циклических ссылок, то за крайне редкими исключениями деструктор будет вызываться вовремя. Но если вы используете ironpython/jython/pypy то ситуация становится совсем печальна.

Для тех кто, вдруг, не в курсе — немного про С++, как пример удобной для программиста реализации деструкторов. С++ гарантирует вызов деструктора у объекта по его уничтожению чтобы не произошло (за исключением полного краха программы/пропадания питания/конца света/etc). Уничтожение наступает либо по выходу из блока в котором определена переменная, либо по удалению объекта с помощью оператора delete при выделении объекта в куче.

    // C++
{
// open file
std::fstream fs(fname, std::ios_base::out);
process_file(fs);
} // file is closed before we get beyong this line, no matter what happened

Гарантированный вызов деструктора — великое программистское благо, позволяющее не заботится о освобождении некоторых ресурсов, не загромождать код всякими with/using/try-finally & Co, и даже для объектов в куче есть всякие unique_ptr. Но все это хорошо работает только в том случае, если объект имел некую локальную область жизни(впрочем unique_ptr/shared_ptr могут и больше). Если же объект выделяется из кучи в одном месте, а освобождается в другом то можно забыть сделать ему delete — получаем утечку памяти. Не смотря на множество способов значительно сократить вероятность такого исхода (например арены) полностью исключить его нельзя.

В качестве решения этой проблемы в современных языках используются сборщики мусора. Периодически они проходятся по памяти, и тем или иным способом удаляют неиспользуемые объекты. Все бы хорошо, но у сборщиков мусора возникают небольшие или большие проблемы с вызовами деструкторов у объектов. Во-первых все сборщики мусора бессильны перед циклическими ссылками. Если объект a ссылается на b, а b ссылается на a, то не понятно в каком порядке вызывать деструкторы. Сначала у a или сначала у b. Если сначала у a, то при вызове деструктора b его ссылка на a будет не валидна и наоборот. Отсутствие информации о смысле взаимосвязей между объектами не дает сборщику мусора вызвать деструкторы в корректном порядке. Копирующие сборщики мусора пошли еще дальше. Они вообще ничего не вызывают, оставляя программиста один на один с этой проблемой и гордо подписываясь «современный сборщик мусора».

Проблема, очевидно, состоит в том что оперативная память это не единственный ресурс требующий освобождения. Еще, как минимум, есть объекты OS, мютексы, транзакции БД, и много много другого. Часть из таких объектов будет через время прикрыта другим кодом (например транзакции БД — но в любом случае они будут создавать лишнюю нагрузку на базу все это время), но объекты OS останутся с процессом до его смерти. А ведь бОльшая часть таких объектов имею локальную область видимости и при гарантированном вызове деструктор был бы идеальным местом для их освобождения. Таким образом переходя от С++ к языкам со сборкой мусора но с недетерменированным вызовом деструкторов мы выигрываем в коде освобождения памяти, но проигрываем на других объектах. Теперь вместо delete «где-то там» вы должны написать dispose/using/with/try-finally прямо тут на каждый объект. Впрочем если файл, например, является не локальным, то и это не поможет. Для примера можно открыть Экслера и глянуть как в яве правильно работать с файлами. Страница ужасного кода ради одного маленького файлика. Не уверен, что такая сборка мусора того стоила.

Итак посмотрим как себя ведут с деструкторами разные варианты питона. В качестве примера возьмем вот такую программу:

Без подсветки синтаксиса

import gc
import sys

class DestTest(object):
def __init__(self, val):
self.val = val

def __del__(self):
sys.stdout.write(str(self) + '.__del__()n')

def __str__(self):
return "DestTest({})".format(self.val)

def __repr__(self):
return "DestTest({})".format(self.val)

def simple_var():
d = DestTest("simple var")

def mdeleted_var():
d = DestTest("manyally deleted var")
del d

def simple_list():
a = [DestTest("simple list")]

def internal_exc():
try:
d = DestTest("exception_func")
raise IndexError()
except:
pass

def exception_func():
d = DestTest("exception_func")
raise IndexError()

def self_ref_list():
a = [DestTest("self-ref list")]
a.append(a)

def cycle_refs():
d1 = DestTest("cycle_ref_obj1")
d2 = DestTest("cycle_ref_obj2")
d3 = DestTest("free_obj")

d1.ref = d2
d2.ref = d1
d2.ref2 = d3

simple_var()
sys.stdout.write("after simple varn")
sys.stdout.write("n")

simple_list()
sys.stdout.write("after simple listn")
sys.stdout.write("n")

mdeleted_var()
sys.stdout.write("after manually deleted varn")
sys.stdout.write("n")

self_ref_list()
sys.stdout.write("after self ref listn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

internal_exc()
sys.stdout.write("after internal excn")

try:
exception_func()
except Exception as x:
pass
sys.stdout.write("after exception funcn")
gc.collect()
sys.stdout.write("after gc.collect()n")
try:
sys.exc_clear()
except AttributeError:
pass
else:
sys.stdout.write("after sys.exc_clear()n")
sys.stdout.write("n")

cycle_refs()
sys.stdout.write("after cycle refsn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

d = DestTest("module var")

import gc
import sys

class DestTest(object):
def __init__(self, val):
self.val = val

def __del__(self):
sys.stdout.write(str(self) + '.__del__()n')

def __str__(self):
return "DestTest({})".format(self.val)

def __repr__(self):
return "DestTest({})".format(self.val)

def simple_var():
d = DestTest("simple var")

def mdeleted_var():
d = DestTest("manyally deleted var")
del d

def simple_list():
a = [DestTest("simple list")]

def internal_exc():
try:
d = DestTest("exception_func")
raise IndexError()
except:
pass

def exception_func():
d = DestTest("exception_func")
raise IndexError()

def self_ref_list():
a = [DestTest("self-ref list")]
a.append(a)

def cycle_refs():
d1 = DestTest("cycle_ref_obj1")
d2 = DestTest("cycle_ref_obj2")
d3 = DestTest("free_obj")

d1.ref = d2
d2.ref = d1
d2.ref2 = d3

simple_var()
sys.stdout.write("after simple varn")
sys.stdout.write("n")

simple_list()
sys.stdout.write("after simple listn")
sys.stdout.write("n")

mdeleted_var()
sys.stdout.write("after manually deleted varn")
sys.stdout.write("n")

self_ref_list()
sys.stdout.write("after self ref listn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

internal_exc()
sys.stdout.write("after internal excn")

try:
exception_func()
except Exception as x:
pass
sys.stdout.write("after exception funcn")
gc.collect()
sys.stdout.write("after gc.collect()n")
try:
sys.exc_clear()
except AttributeError:
pass
else:
sys.stdout.write("after sys.exc_clear()n")
sys.stdout.write("n")

cycle_refs()
sys.stdout.write("after cycle refsn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

d = DestTest("module var")

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

python2.7

    DestTest(simple var).__del__()
after simple var

DestTest(simple list).__del__()
after simple list

DestTest(manyally deleted var).__del__()
after manually deleted var

after self ref list
DestTest(self-ref list).__del__()
after gc.collect()

after exception func
after gc.collect()
DestTest(exception_func).__del__()
after sys.exc_clear()

after cycle refs
after gc.collect()

Exception AttributeError: "'NoneType' object has no attribute 'stdout'" in
ignored

Более-менее. Деструктор у циклических ссылок не был вызван, как и ожидалось. Для уничтожения объектов, связанных с исключением, дошедшем до уровня модуля приходится вызывать sys.clear_exc(), в остальных случаях с исключениями все ок. Интересно, что питон освободил sys.stdout раньше, чем переменную d, в итоге чего ее деструктор отработал не корректно (впрочем это поведение не всегда повторяется).

python3.3

    DestTest(simple var).__del__()
after simple var

DestTest(simple list).__del__()
after simple list

DestTest(manyally deleted var).__del__()
after manually deleted var

after self ref list
DestTest(self-ref list).__del__()
after gc.collect()

DestTest(exception_func).__del__()
after exception func
after gc.collect()

after cycle refs
after gc.collect()

Exception AttributeError: "'NoneType' object has no attribute 'stdout'" in
ignore

Почти то-же самое, что и 2.7, только sys.exc_clear() больше не нужно.

ironpython2.7

    after simple var

after simple list

after manually deleted var

after self ref list
DestTest(self-ref list).__del__()
DestTest(manyally deleted var).__del__()
DestTest(simple list).__del__()
DestTest(simple var).__del__()
after gc.collect()

after exception func
DestTest(exception_func).__del__()
after gc.collect()
after sys.exc_clear()

after cycle refs
DestTest(free_obj).__del__()
DestTest(cycle_ref_obj2).__del__()
DestTest(cycle_ref_obj1).__del__()
after gc.collect()

DestTest(module var).__del__()

Удаляет все, но только при сборке мусора, до те
х пор все объекты живут где-то в памяти.

И — встречаем победителя. Сама лаконичность или мир всем вашим деструкторам от нашей Java и копирующего сборщика мусора: jython2.7a2 — Java HotSpot(TM) Client VM (Oracle Corporation) on java1.7.0_07

    after simple var

after simple list

after manually deleted var

after self ref list
after gc.collect()

after exception func
after gc.collect()
after sys.exc_clear()

after cycle refs
after gc.collect()

Правда в ява есть и другие варианты сборщика мусора, может там чуть по-лучше.

А вывод все тот-же. По возможности — используйте with. По невозможности — попробуйте поправить код, так что-бы можно было использовать with. Иначе — аккуратно вызывайте деструкторы руками

P.S. Как я люблю, когда мне предлагают делать какую-то рутинную работу в коде «аккуратно».

Ссылки:
          koder-ua.blogspot.com/2012/10/python-with.html
          en.wikipedia.org/wiki/Region-based_memory_management

Исходники этого и других постов со скриптами лежат тут — github.com/koder-ua. При использовании их, пожалуйста, ссылайтесь на koder-ua.blogspot.com.

Автор: konstantin danilov

Создаём QR коды на Python (Перевод)

Как-то раз я задумался: было бы забавно создать программу при помощи wxPython, которая генерирует QR коды и затем отображает их на экране. Поскольку, как понятно, я хотел написать её на Python, то после недолгих поисков я обнаружил трёх кандидатов на это дело:
Я воспользовался python-qrcode и pyqrnative, так как они прекрасно работают как на Windows, так и на Mac и Linux. Кроме того, они не требуют ничего больше, только Python Imaging Library. Проект pyqrcode требует дополнительных модулей и не работает на Windows, так что я даже не стал с ним заморачиваться. В итоге я взял старый код моего  Photo Viewer и немного его переиначил, чтобы получить генератор QR кодов. Если интересно — давайте поподробнее!

Приступаем

Как я уже упомянул в начале, Вам понадобится Python Imaging Library. Мы будем использовать wxPython для создания GUI, так что без него тоже не обойтись. И кроме того Вам понадобится загрузить python-qrcode и pyqrnative. Основное отличие, которое я обнаружил между ними — python-qrcode гораздо быстрее создаёт изображение и изображение именно привычного нам вида. По каким-то причинам, pyqrnative требует больше времени и создаёт более плотно выглядящие QR коды. Возможно, у каждого из проектов есть опции, меняющие вид кодов, но у обоих проектов отвратительная документация. В итоге пришлось копаться в исходниках больше, чем когда-либо.

Создаём QR коды

Итак, как только мы установили всё, что нам нужно, мы можем запустить следующий код и посмотреть, на что же способен Python:
import os
import wx
 
try:
import qrcode
except ImportError:
qrcode = None
 
try:
import PyQRNative
except ImportError:
PyQRNative = None
 
########################################################################
class QRPanel(wx.Panel):
""""""
 
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
self.photo_max_size = 240
sp = wx.StandardPaths.Get()
self.defaultLocation = sp.GetDocumentsDir()
 
img = wx.EmptyImage(240,240)
self.imageCtrl = wx.StaticBitmap(self, wx.ID_ANY,
wx.BitmapFromImage(img))
 
qrDataLbl = wx.StaticText(self, label="Текст для перевода в QR код:")
self.qrDataTxt = wx.TextCtrl(self, value="http://www.mousevspython.com", size=(200,-1))
instructions = "Имя файла с QR кодом"
instructLbl = wx.StaticText(self, label=instructions)
self.qrPhotoTxt = wx.TextCtrl(self, size=(200,-1))
browseBtn = wx.Button(self, label='Изменить место для сохранения')
browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
defLbl = "Сохраняется по умолчанию в: " + self.defaultLocation
self.defaultLocationLbl = wx.StaticText(self, label=defLbl)
 
qrcodeBtn = wx.Button(self, label="Создать QR при помощи qrcode")
qrcodeBtn.Bind(wx.EVT_BUTTON, self.onUseQrcode)
pyQRNativeBtn = wx.Button(self, label="Создать QR при помощи PyQRNative")
pyQRNativeBtn.Bind(wx.EVT_BUTTON, self.onUsePyQR)
 
# компоновщик
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
qrDataSizer = wx.BoxSizer(wx.HORIZONTAL)
locationSizer = wx.BoxSizer(wx.HORIZONTAL)
qrBtnSizer = wx.BoxSizer(wx.VERTICAL)
 
qrDataSizer.Add(qrDataLbl, 0, wx.ALL, 5)
qrDataSizer.Add(self.qrDataTxt, 1, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY),
0, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(qrDataSizer, 0, wx.EXPAND)
self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
locationSizer.Add(instructLbl, 0, wx.ALL, 5)
locationSizer.Add(self.qrPhotoTxt, 0, wx.ALL, 5)
locationSizer.Add(browseBtn, 0, wx.ALL, 5)
self.mainSizer.Add(locationSizer, 0, wx.ALL, 5)
self.mainSizer.Add(self.defaultLocationLbl, 0, wx.ALL, 5)
 
qrBtnSizer.Add(qrcodeBtn, 0, wx.ALL, 5)
qrBtnSizer.Add(pyQRNativeBtn, 0, wx.ALL, 5)
self.mainSizer.Add(qrBtnSizer, 0, wx.ALL|wx.CENTER, 10)
 
self.SetSizer(self.mainSizer)
self.Layout()
 
#----------------------------------------------------------------------
def onBrowse(self, event):
""""""
dlg = wx.DirDialog(self, "Выберите папку:",
style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self.defaultLocation = path
self.defaultLocationLbl.SetLabel("Сохраняем в: %s" % path)
dlg.Destroy()
 
#----------------------------------------------------------------------
def onUseQrcode(self, event):
"""

https://github.com/lincolnloop/python-qrcode

"
""
qr = qrcode.QRCode(version=1, box_size=10, border=4)
qr.add_data(self.qrDataTxt.GetValue())
qr.make(fit=True)
x = qr.make_image()
 
qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
img_file = open(qr_file, 'wb')
x.save(img_file, 'JPEG')
img_file.close()
self.showQRCode(qr_file)
 
#----------------------------------------------------------------------
def onUsePyQR(self, event):
"""

http://code.google.com/p/pyqrnative/

"
""
qr = PyQRNative.QRCode(20, PyQRNative.QRErrorCorrectLevel.L)
qr.addData(self.qrDataTxt.GetValue())
qr.make()
im = qr.makeImage()
 
qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
img_file = open(qr_file, 'wb')
im.save(img_file, 'JPEG')
img_file.close()
self.showQRCode(qr_file)
 
#----------------------------------------------------------------------
def showQRCode(self, filepath):
""""""
img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
# масштабируем изображение
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.photo_max_size
NewH = self.photo_max_size * H / W
else:
NewH = self.photo_max_size
NewW = self.photo_max_size * W / H
img = img.Scale(NewW,NewH)
 
self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
self.Refresh()
 
 
########################################################################
class QRFrame(wx.Frame):
""""""
 
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="QR Code Viewer", size=(550,500))
panel = QRPanel(self)
 
if __name__ == "__main__":
app = wx.App(False)
frame = QRFrame()
frame.Show()
app.MainLoop()
Код для изменения и отображения картинки разъяснён в одной из предыдущих статей (не переведена), так что единственное, что может вызвать у Вас непонимание — два метода создания кодов: onUseQrcode и onUsePyQR. Я всего лишь взял примеры с их сайтов и немного поменял их, чтобы сгенерировать изображение с кодом. Они достаточно самоочевидны, но почти не документированны, так что я не смогу сказать Вам, что же тут происходит. И, хотя оба проекта страдают на момент написания от отсутствия документации, код, тем не менее работает:
Как можно видеть — вполне стандартный код. А этот, созданный PyQRNative, выглядит гораздо более «плотным»:
Оба кода были корректно распознаны моим телефоном на  Android. Так что если Вам надо создать для вашего проекта QR код — я надеюсь, эта статья Вам поможет!

Обновление от 21/05/2012

Один из моих читателей (Mike Farmer) сообщил мне недавно, что в PyQRNative “первый аргумент — размер контейнера, а второй — избыточность / коррекция ошибок”. У меня были такие подозрения по поводу второго аргумента, но я не знаю, что делают эти уровни коррекции ошибок. К счастью, Mr. Farmer объяснил это мне: Если уровень коррекции ошибок низкий, то смазывание кода или его намокание приведёт его в нечитабельное состоянии. Если же Вы будете использовать более высокий уровень коррекции, то за счёт добавления / дублирования данных внутри кода, он сможет пережить даже потоки слёз. Более того, при высоком уровне коррекции ошибок Вы можете даже наложить на код дополнительное изображение, и он  это переживёт. В любом случае, если Вы изменяете первый параметр, Вы можете изменить размер кода. Зачем? Ну, чем больше информации Вы хотите засунуть в код, тем больше он должен быть. Mr. Farmer предложил забавный тестовый код, который позволяет вычислить минимальный возможный для вашей информации размер кода:

import PyQRNative
 
def makeQR(data_string,path,level=1):
quality={1: PyQRNative.QRErrorCorrectLevel.L,
2: PyQRNative.QRErrorCorrectLevel.M,
3: PyQRNative.QRErrorCorrectLevel.Q,
4: PyQRNative.QRErrorCorrectLevel.H}
size=3
while 1:
try:
q = PyQRNative.QRCode(size,quality[level])
q.addData(data_string)
q.make()
im=q.makeImage()
im.save(path,format="png")
break
except TypeError:
size+=1

Исходник

Автор: Ishayahu Lastov

Релиз Demakein 0.2: Шалмей (Перевод)

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

Автор: Ishayahu Lastov

Программирование работы с SSH при помощи Paramiko (Перевод)

OpenSSH — это вездесущий метод удалённого безопасного доступа к машине и передачи файлов. Многие — системные администраторы, инженеры автоматизации тестов, веб-разработчики и другие люди используют этот методы ежедневно. Написание скриптов для ssh на Python может быть тяжёлым занятием, но модуль Paramiko позволяет решить эту задачу проще.
Это репринт статьи, написанной для Python Mag­a­zine в колонку Com­pletely Dif­fer­ent и опубликованной в октябрьском выпуске 2008 года. Тут она приводится в оригинальной форме, со всеми ошибками и т.д.

Читать