Показаны сообщения с ярлыком python. Показать все сообщения
Показаны сообщения с ярлыком python. Показать все сообщения

пятница, 24 января 2014 г.

Вывод локальных переменных функции с ошибкой

Иногда бывает необходимость посмотреть локальные переменные функции в python, которая вызвала Exception в самом верху стека вызовов.

Проще всего сделать это, воспользовавшись штатным модулем inspect внутри блока except:

inspect.trace()[-1][0].f_locals

вторник, 6 ноября 2012 г.

Трекинг страниц в Google Analytics на серверной стороне

Для недавно сделанного мной микросервиса shares.datagreed.ru мне захотелось отслеживать переходы по разным адресам api. Как это сделать, если Google Analytics интегрируетсяв страницу посредством JS?

Я тоже не знал. Поиск привел меня к официальной библиотеке на PHP для мобильных сайтов, на которых предполагалось не использовать яваскрипт.

Дальнейшие мои изыскания вылились в небольшую библиотеку для питона, которая позволяет использовать GA со стороны сервера. Штатно в библиотеку влючены классы для Django и Flask. Класс для Flask в данный момент развернут и полностью функционирует у меня на shares.datagreed.ru, класс для Django я пока что не тестировал.

Проект живет на здесь, в моем битбакете.

понедельник, 13 августа 2012 г.

Django на uWSGI и nginx

Старые способы запуска сервера, которыми я пользовался обычно, не предполагали, что на сервере больше одного приложения, да и в принципе сейчас уже несколько архаичны. В свете того, что Django с версии 1.4 официально перешла на WSGI, я решил набросать небольшой гайд по разворачиванию приложения на связке nginx+uWSGI.

среда, 8 августа 2012 г.

virtualenv wrapper - удобная обертка над virtualenv

Что такое virtualenv никакому разработчику на python и рассказывать не нужно, но не все знают (или это только я как-то пропустил), что есть удобная обертка над ним, позволяющая производить все операции с виртуальным окружением гораздо быстрее. Называется соответственно - virtualenv wrapper.

Ставится, как обычно, через pip:
$ pip install virtualenvwrapper

После установки нужно сделать пару манипуляций для его конфигурирования.

Декоратор для автоматической регистрации сигналов Django

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

from functools import wraps
from django.db.models.signals import pre_save
from django.db.models.signals import post_save

def autoconnect_signals(cls):
    """ 
    Class decorator that automatically connects pre_save/post_save signals on 
    a model class to its pre_save_handler()/post_save_handler() methods.
    """
    def connect(signal, func):
        cls.func = staticmethod(func)
        @wraps(func)
        def wrapper(sender, *args, **kwargs):
            return func(kwargs.get('instance'), args, kwargs)

        signal.connect(wrapper, sender=cls)
        return wrapper

    if hasattr(cls, 'pre_save_handler'):
        cls.pre_save_handler = connect(pre_save, cls.pre_save_handler)

    if hasattr(cls, 'post_save_handler'):
        cls.post_save_handler = connect(post_save, cls.post_save_handler)
    
    return cls

Пользоваться декоратором просто. Создаете модель, в ней делаете методы pre_save_handler и/или post_save_handler и оборачиваете класс декоратором:

@autoconnect_signals    
class Book(models.Model):    
    
    #...
    name = models.SlugField(_("Name"), max_length = 150, unique = True)

    def pre_save_handler(self,*args, **kwargs):
        u"""Сигнал, получаемый перед сохранением модели."""
        print self.name #в self передается сам инстанс модели.
                        #Остальное есть в аргументах.

    def post_save_handler(self,*args, **kwargs):
        u"""Сигнал, получаемый после сохранения модели."""
        print self.name

Вот и все. pre_save_handler и post_save_handler сами вызовуться по соответствующим сигналам сохранения модели.


пятница, 27 июля 2012 г.

Docstring coverage - покрытие python-кода документацией

Как проверить, что python-разработчики хорошо задокументировали код, кроме как просматривать все руками или генерировать pydoc'ом документацию и сравнивать с исходниками? Вот и я не нашел никакого решения, пока случайно не натолкнулся на старый-престарый скрипт, который вдохновил меня на форк и последующую доработку.

В результате у меня получился полезный инструмент Docstring coverage, позволяющий оценить процентное покрытие кода документацией по всему проекту.

Пускается так:
docstring-coverage [опции] <путь к папке или модулю>


Среди опций есть -m, которая заставляет утилиту пропускать __магические__ методы python и -v, позволяющая настроить уровень "болтливости" вывода от 0 до 3.

Можно импортировать в рабочий проект использовать для получения статистики по покрытию:

import docstringcoverage
cover_results = docstringcoverage.get_docstring_coverage('somefolder/somefile.py')

Вся документация с примерами есть на странице проекта.


среда, 18 июля 2012 г.

django-paramfield

Решил выложить в паблик поле ParamField для Django, которое изначально использовалось мной в одном старом проекте, который, к сожалению, канул в лету из-за недостатка финансирования. Но поле достаточно полезное и используется мной сейчас в других проектах на Django.

Исходники и документацию можно взять в на странице проекта в bitbucket.

Расскажу вкратце об этом поле.

среда, 27 июня 2012 г.

Установка psycopg2 на OSX Server

По долгу службы понадобилось установить psycopg2 для работы с PostgreSQL под OSX. Задача оказалась нетривальной.

Если вкратце, то ставить его нужно так:

PATH=$PATH:/Library/PostgresPlus/9.1/bin/ sudo easy_install psycopg2

После чего поправить симлинки на libssl и libcrypto (если их версии ниже 1.0.0, что скорее всего так), иначе psycopg при импорте будет выпадать с ошибкой:


sudo ln -f /Library/PostgreSQL/9.1/lib/libcrypto.1.0.0.dylib /usr/lib/libcrypto.dylib


sudo ln -f /Library/PostgreSQL/9.1/lib/libssl.1.0.0.dylib /usr/lib/libssl.dylib

И вот теперь, наконец, psycopg2 будет нормально импортироваться.

понедельник, 2 января 2012 г.

Шаблон для геттера кешируемой переменной в Django

Когда пишешь под Django, часто приходится писать геттеры каких-то переменных модели, которые в первый раз извлекаются из базы данных, а потом сохраняются в приватном свойстве объекта (похожим образом работает стандартный джанговский LazyUser из AuthenticationMiddleware, например). Решил поделиться своим шаблоном для этого случая, который подойдет для PyDev или любого другого редактора, основанного на Eclipse:


def ${method_name}(self, try_cached = True, save_cache = True):
                
    result = None
        
    if try_cached:
        #если разрешено отдавать из кеша, отдаем
        if hasattr(self, '_cached_${method_name}'):        
            return self._cached_${method_name}
        
    ${cursor}             
        
    if save_cache:
        #если надо закешировать в объекте, сохраним в приватном свойстве
        self._cached_${method_name} = result
        
    return result

среда, 4 мая 2011 г.

Отправка писем с DKIM-подписью в Django

Если вы вдруг обеспокоились тем, чтобы ваши письма не попадали в черные списки почтовых сервисов и решили-таки прикрутить DKIM-подписи к своим письмам в Django, то нет ничего проще.

Начиная с версии 1.2, Django стала поддерживать почтовые backend'ы, что существенно облегчило жизнь разработчику в этом плане. Все, что требуется, так это установить pydkim и создать модуль с бекендом, например <ваше_приложение>/mail/backends.py:

from django.core.mail.backends.smtp import EmailBackend
from django.conf import settings
import dkim

class DKIMBackend(EmailBackend):
         u'''
         Бекенд, позволяющий вставлять в письма DKIM-подпись.
         Требует определения переменных в settings:
                 DKIM_SELECTOR - селектор, все что до точки в  "selector._domainkey.example.com"
                 DKIM_DOMAIN - домен например, "example.com"
                 DKIM_PRIVATE_KEY - приватный DKIM-ключ. Полностью, включая "-----BEGIN RSA PRIVATE KEY-----" и т.п.
         @requires: python-dkim, есть на http://hewgill.com/pydkim
         '''
     def _send(self, email_message):

         if not email_message.recipients():
             return False
         try:
             message_string = email_message.message().as_string()
             signature = dkim.sign(message_string,
                                   settings.DKIM_SELECTOR,
                                   settings.DKIM_DOMAIN,
                                   settings.DKIM_PRIVATE_KEY)
             self.connection.sendmail(email_message.from_email,
                     email_message.recipients(),
                     signature+message_string)
         except:
             if not self.fail_silently:
                 raise
             return False
         return True

Останется только сменить в settings.py параметр EMAIL_BACKEND на "<ваше_приложение>.mail.backends.DKIMBackend" и прописать там же DKIM_SELECTOR, DKIM_DOMAIN и DKIM_PRIVATE_KEY (смотрите докстринг в коде). Сгенерировать недостающие ключи можно тут.

понедельник, 11 апреля 2011 г.

Взвешенный выбор в Python

Очень часто встречаю людей, которые зачем-то пытаются сделать взвешенный выбор в питоне, создавая огромных размеров массивы с повторяющимися значениями и потом выбирая из них результат с помощью random.choice. Вариант, конечно, логичный и простой в реализации, но... как же он тормозит и сколько памяти расходует. Что если у меня у одного элемента вес 5000, а у другого 1? Зачем мне ради одной операции строить массив длинной в 5001 элемент? :)

Конечно же, есть варианты гораздо проще и изящнее. Списывайте:


import random 

def weighted_choice(choices):
    u'''
    Взвешенный псевдослучайный выбор. Чем выше вес, тем выше шанс выпадения значения.
    @param choices: список или кортеж пар вида: (   ('choice', 14), ('choice2', 11), ... (<значение>, <вес>  ) 
    '''
    total = sum(w for c,w in choices)
    r = random.uniform(0, total)
    upto = 0
    for c, w in choices:
        if upto+w > r:
            return c
        upto += w
    assert False, "Shouldn't get here"

Работает вот так:

test_choices = ( ('Foo', 8), ('Bar', 3), ('god damn!', 1) )
for i in xrange(10):
    print weighted_choice(test_choices)