Analitycs

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

понедельник, 27 апреля 2020 г.

Pyenv in ZSH: Failed to activate virtualenv

В zsh в MacOSX Catalina при попытке активировать pyenv вылезает
Failed to activate virtualenv. Perhaps pyenv-virtualenv has not been loaded into your shell properly. Please restart current shell and try again.
Лечить - добавив 3 строчки
export PYENV_VIRTUALENV_DISABLE_PROMPT=1
eval "$(pyenv virtualenv-init -)"
eval "$(pyenv init -)"
в ~/.zshrc Итоговый блок должен выглядеть как-то так
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
export PYENV_VIRTUALENV_DISABLE_PROMPT=1
eval "$(pyenv virtualenv-init -)"
eval "$(pyenv init -)"

понедельник, 25 июня 2012 г.

Как перемещать 3D камеру в Blender на MacBook?

Занесла меня тут нелегкая в 3D - с которым лет 10-15 уже дела не имел ;-) Последний раз чего то пытался рисовать еще в 90-е годы в институте в VRML. Понял, что не мое - но тут - НАДО! ;-)

Скачал Blender под MacOS, поставил и вот нужно мне некоторые предметы рассмотреть в трехмерке весьма тщательно и подробно - ан нет.

Ведь на ноуте моем - ни трехкнопочной мышки - ни даже однокнопочной, если честно ;-) , ни  NumPad, а с помощью touchpad - могу только крутить по одной оси, перемещать камеру вверх-вниз, вправо-влево и приближать-удалять.

Все, думаю - кранты - необходимый мне предмет ну никак не рассмотреть... ;-(

Рецепт

Идем в User Preferences -> Input и включаем галки

[v] Emulate 3-button mouse
[v] Emulate Numpad

В итоге с помощью цифровых клавиш  2<->8 и  4<->6 можно хоть как-то крутить камеру в трехмерке. Неудобно - но хоть как-то
И да, оказывается тут еще и на Python можно писать... Ляпота ;-) ;-)

понедельник, 21 мая 2012 г.

Memcache не запоминает данные больше чем на 30 дней (expiration time)

При при попытке сохраненить данных в Memcache на срок больше чем на 30 дней (2 592 000 секунд) - данные не запоминаются ВООБЩЕ.

Кто виноват и что делать?

Сохранять в timestamp + expiration time - на Tornado/Python это выглядит как-то так

from datetime import datetime, timedelta
import tornado.escape

class MyApplication():

    def get_expire_time(self):
        start = datetime.now()
        session_length = self.settings['session_time']
        
        delta = timedelta(seconds=session_length)
        expires = start + delta
        return expires

    def save_session(self, data)
        end_time = time.mktime(self.get_expire_time().timetuple())
            
        self.mc.set(key, tornado.escape.json_encode(data), end_time)

Навеяно StackOverflow

четверг, 17 мая 2012 г.

Корректная кодировка в email - Python+UTF8

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

Вчера понадобился аналогичный вариант для Python c UTF-8 с использованием quopri

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import quopri

def QuoHead(String):
    s = quopri.encodestring(String.encode('UTF-8'), 1, 0)
    return "=?utf-8?Q?" + s.decode('UTF-8') + "?="

FIOin = "Андрей Петрович"
emailout = "some@test.ru"
emailin = "some2@test.ru"

msg = MIMEMultipart()
msg["Subject"] = QuoHead("Добрый день " + FIOin).replace('=\n', '')
msg["From"] = (QuoHead("Ирина Федоровна") + "  <" + emailout + ">").replace('=\n', '') 
msg["To"] = (QuoHead(FIOin) + "  <" + emailin + ">").replace('=\n', '')
m = """Добрый день.
  Это тестовое письмо.
Пожалуйста, не отвечайте на него."""


text = MIMEText(m.encode('utf-8'), 'plain', 'UTF-8')
msg.attach(text)
print(msg.as_string())


Найдено тут.

воскресенье, 29 апреля 2012 г.

Востребованность Python Tornado/Twisted

Кстати - об асинхронных фреймворках и их востребованности.

Решил пробить количество вакансий на асинхронные фреймворки на Python - Twisted и Tornado.

Далее факты - и ничего кроме фактов.

Linkedin

  • python twisted - 10 вакансий против 1026 участников
  • python tornado - 14 вакансий  против 815 участников

HH

  • python tornado - 0
  • python twisted - 1 вакансия

Moikrug

  • python twisted - 2 вакансии
  • python tornado  - 1 вакансия

То есть в мире - на 1 открытую вакансию на асинхронный питон приходится 100 кандидатов для twsted и 59 - tornado.

Что интересней -  linkedin по запросу python django дает 208 вакансий 18469 участников - на 1 вакансию 71 потенциальный кандидат.

Что как-бы намекает...

Ну а php -  вообще полный ахтунг - 2060 вакансий и 634347 кандидатов -> 308 человек на одну позицию.

Кстати, хороший Python-dev с Торнадой/Twisted по прежнему - ищется.

суббота, 17 марта 2012 г.

MD5 проверка PHP, Python и MySQL

Несколько лет поймал одну особенность - md5 суммы посчитанные в PHP и MySQL отличались(!!!) - но хоть убей не помню на каких версиях.

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

PHP

php -r "echo md5('domains.txt');"
aeae2f628c54f10054e5c70076e1237b

md5sum

# echo -n "domains.txt" | md5sum
aeae2f628c54f10054e5c70076e1237b 

Python

# echo "import md5;print md5.md5(\"domains.txt\").hexdigest()" | python
aeae2f628c54f10054e5c70076e1237b

MySQL

SELECT MD5('domains.txt');

aeae2f628c54f10054e5c70076e1237b

Уф, отпустило ;-) Приступ паранойи навеян вот этой заметкой.

четверг, 15 марта 2012 г.

Logging в Tornado

После большого рефакторинга проекта на Tornado пропало логгирование в stdout. То есть ошибки писались только в stderr, который выводился в лог сидящего уровнем выше сервиса  Supervisord (который за сервисом, собственно говоря, и наблюдал).

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

tornado.options.parse_command_line()

То есть должно быть примерно так

def main(): 
    application = tornado.web.Application([ 
        (r"/", MainHandler), 
    ]) 
    # this line will setup default logging no matter if you use command options 
    tornado.options.parse_command_line()
    logging.info("starting torando web server") 
    http_server = tornado.httpserver.HTTPServer(application) 
    http_server.listen(8888) # hardcoded port 
    tornado.ioloop.IOLoop.instance().start() 

После восстановления строчки-"беглянки" лог торнадовского приложения выглядит как положено


mneradkov@mneradkov-thinkpad:~/workspace/mysuperproject$ ./webapp.py 
[I 120315 16:33:28 mixins:28] [INFO] Directory [/home/mneradkov/workspace/mysuperproject]
[I 120315 16:33:28 mixins:28] [INFO] Connected to MySQL [xx.xx.xx.xx:xxxx]
[I 120315 16:33:28 mixins:28] [INFO] Connected to memcache [127.0.0.1:11211]
[I 120315 16:33:28 mixins:28] [INFO] 
[I 120315 16:33:28 mixins:28] [INFO] Server [My Super Server] version [X.Xalpha] binded to [127.0.0.1:11100]
[I 120315 16:33:28 webapp:37] Starting I/O loop to serve requests...

среда, 29 февраля 2012 г.

Как правильно деплоить Python сервис запущеный через supervisord?

Иногда supervisord заглючивает и он неправильно определяет необходимость перекомпиляции pyc файлов при обновлении python файлов, что при деплое новой версии сервиса на Python приводит к забавным, но неприятным эффектам

Рекомендуемый порядок действия

# supervisorctl stop my_super_service

Теперь обновляем файлы, затем РУЧКАМИ удаляем pyc

# find path_to_my_service -type f -name "*.pyc" -delete
# supervisorctl start my_super_service

При использовании сервисов автоматического деплоя - имеет смысл повесить эти операции на автоматические хуки - до и после деплоя.

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

Ищу Python developer - Junior, СПб


Ищу будущих коллег и хороших людей:


Обязанности:

Разработка и сопровождение серверной части проекта Омлет.ру (легальный видео-контент);

Что хотим  от вас?

  • Хороший опыт работы с бекендами сайтов
  • Опыт разработки на Python или устойчивое желание его изучить - при наличии знаний других языков (С#, PHP, Perl , Ruby);
  • Знание MySQL;
  • Аналитический склад ума,
  • Коммуникабельность,
  • Знание основ ООП,
  • Самообучение и саморазвитие,
  • Чувство юмора и адекватность - обязательно.

Вы кандидат нашей мечты, если...

  • уже работали с Python
  • есть опыт работы с no-SQL решениями,
  • есть опыт работы с высоконагруженными веб-сервисами/сайтами - не стесняйтесь показывать портфолио - нам это интересно;
  • владеете хорошей математической базой;
  • знаете и применяете различные языки программирования, новейшие технологии,
  •  владеете английским языком
Если есть практический опыт Tornado/Twisted - вам большой такой плюс, перекрывающий многие минусы.

Что мы предлагаем?

  • Возможность забросить PHP - вам еще не надоело? 
  • Работу в большом и интересном проекте
  • Офис - ст. метро Василеостровская + 5 минут ходьбы - и то прогулочным шагом 
  • Неординарные задачи
  • Полностью белое оформление и ЗП
  • Повышенный отпуск - как компенсация за ненормированный рабочий день
  • Корпоративную симку для связи
  •  После испытательного срока - ДМС

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

пятница, 24 февраля 2012 г.

Tornado AsyncHTTPTestCase: AssertionError: Async operation timed out after 5 seconds

Тестирование в Торнадо (особенно асинхронных сервисов) имеет много неочевидных нюансов.

Ситуация

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

class MyAsyncHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self, *args, **kwargs):
        try:
            # тут чего то берем с внешнего сервера
            self.auth_request({})
        except Exception, ex:
            self.error('Async - %s'%ex)
            self.reply(None)


    def auth_request(self, params, callback=None):
        url = self.application.settings['external_service'] + '?' + urlencode(params)

        if not callback:
            callback = self._on_load

        http = httpclient.AsyncHTTPClient()
        http.fetch(url, callback, validate_cert=False)

    def _on_load(self, response):
        # обрабатываем полученные результат        
        pass

То при написании юнит-тестов через AsyncHTTPTestCase в подобном ключе по официальной документации(!!!)

class MyTestServer(AsyncHTTPTestCase):
    def get_host(self):
        return 'http://%s:%d'%(SERVER['host'], SERVER['port'])
    
    def get_app(self):
        myserver = MyServer()
        myserver.run(SERVER['host'], SERVER['port'])        

        return myserver.app # приложение Tornado
    
    def get_response_obj(self, url, **kwargs):
        if len(kwargs) > 0:
            url += '?' + urllib.urlencode(kwargs)
        
        logging.info('Request [%s]'%url)

        self.http_client.fetch(url, self.stop)
        response = self.wait()
        
        print response

        if response.body: 
            return response.body
        
        return None

При запуске тестов начинаем ловить потрясающие спецэффекты,

Traceback (most recent call last): 
  File "/home/xxxx/test_wait.py", line 9, in test_1 
    self.wait(timeout = 5) 
  File "/xxxxx/site-packages/tornado/ 
testing.py", line 169, in timeout_func 
    timeout) 
AssertionError: Async operation timed out after 5 seconds 

Лечится это добавлением в класс теста переопределенного метода get_new_ioloop

class MyTestServer(AsyncHTTPTestCase):
    ....
    
    def get_new_ioloop(self): 
        return ioloop.IOLoop.instance()  

Такие дела...

Тема в гуглогруппеGist с примером

суббота, 11 февраля 2012 г.

Как проверить string ли переменная в Python? - isinstance(val, basestring)

Когда нужно проверить - строка ли переменная в Python - есть небольшой подводный камень, о котором иногда забывают...

Все ли помнят что строки в питоне разные? (например, str и  unicode), так что для общей проверки нужно использовать isinstance(val, basestring):

 Например

$ python
Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

>>> isinstance('abcd', str)
True
>>> isinstance('abcd', basestring)
True
>>> isinstance(u'abcd', str)
False
>>> isinstance(u'abcd', basestring)
True

пятница, 10 февраля 2012 г.

Как запускать периодические задачи на Tornado? PeriodicCallback!

Продолжая тему периодических задач в Twisted, в Tornado это делается немного по другому - с помощью специального tornado.ioloop.PeriodicCallback.

Примерчик - если кому пригодится

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tornado.web
import tornado.httpserver
import tornado.ioloop

PERIOD = 30 # в минутах

class MyApplication(tornado.web.Application):

  def period_run(self):
    # чего-то тут делаем
    pass

...

app = MyApplication(handlers=urls, **settings)

http_server = tornado.httpserver.HTTPServer(app, xheaders=True)
 
loop = tornado.ioloop.IOLoop.instance()
period_cbk = tornado.ioloop.PeriodicCallback(app.period_run, 1000*60*PERIOD, loop)

period_cbk.start()

loop.start()

вторник, 31 января 2012 г.

Python/PHP - запятая - казнить нельзя помиловать

Сегодня ловлю баг в сервисе - в JSON-результате вместо поля с integer  выдается список элементов-integer, то есть вместо

data : {
  'id' : 1
}

упорно получаю

data : {
  'id' : [1]
}

Перепроверяю код 20 раз - ничего не могу понять... вроде все правильно, должен быть идентификатор ОДИН, но хоть ты тресни - список. Подвисаю на несколько минут, иду попить кофе, снова смотрю в код...

Через несколько минут доходит - начинаю истерически хохотать

def bla-bla(self, args): 
    data = {
      'bla-bla' : bla_bla,
      'name' : name,
      'id' : temp_id,
    }
  return data


Правлю один символ - все работает. После хохота начинаю вспоминать хорошего PHP team-leader, с которым работал много-много лет назад.

Он был большим аккуратистом в плане чистоты форматирования кода и железнобетонно приучил меня расставлять меня запятые в ассоциативных массивах в КАЖДОМ элементе, включая последний.  Так как в случае необходимости быстро переставить элементы в коде - не пришлось бы искать потенциальную ошибку c пропущенной запятой.

А для Python - это собственно - сокращенный формат списка-последовательности.

Так что вот вам прекрасный пример того, что старые привычки часто вредят, а иногда - вредят ОЧЕНЬ сильно. "Казнить нельзя помиловать" (с)   ;-)

Ну и верный вариант - БЕЗ запятой ;-)

def bla-bla(self, args): 
    data = {
      'bla-bla' : bla_bla,
      'name' : name,
      'id' : temp_id
    }
  return data

пятница, 27 января 2012 г.

Python implode/join - TypeError: sequence item 0: expected string, int found

Одна из частых функций в PHP проектах - implode/explode.

На Python (по идее/по мануалу) это делается так

tmp = [1,2,3,4,5]
','.join( tmp )

Но в результате - получаем ошибку TypeError: sequence item 0: expected string, int found

Проблема - в том, что у нас числа, а не строки

 В итоге - нужно так

','.join( map( str, tmp ) )
'1,2,3,4,5'

Но вот пресловутое неявное приведение типов-то в PHP как помогает, а? ;-)

четверг, 19 января 2012 г.

Python PIP не работает под MacOS X 10.7 Lion - pkg_resources.DistributionNotFound: pip==1.0.2

На новой машинке чудеса с PIP и MacPorts следующего вида - система не видит установленного PIP через MacPorts

Диагноз

$ pip
Traceback (most recent call last):
  File "/usr/local/bin/pip", line 5, in 
    from pkg_resources import load_entry_point
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 2603, in 
    working_set.require(__requires__)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 666, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/pkg_resources.py", line 565, in resolve
    raise DistributionNotFound(req)  # XXX put more info here
pkg_resources.DistributionNotFound: pip==1.0.2

Самолечение

Не совсем грамотно, но вроде помогло - установка PIP через easy_install
curl -O http://python-distribute.org/distribute_setup.py
sudo python distribute_setup.py
sudo easy_install pip

вторник, 27 декабря 2011 г.

Twisted или Tornado - без разницы - все идиоты

Внимание - Важное сообщение для блоггеров-питонистов!


Если у вас появилась блестящая идея попробовать новый сфероконический тест, который показывает как Tornado уделывает Twisted - то выдохните. Выключите компьютер, погуляйте на улице и заодно - пересмотрите главную цель вашей жизни. Интернету не нужен еще один бессмысленный график производительности.

Похоже, что уже всем стало ясно что "матч века" - Twisted.web против Tornado от Friendfeed показал, что ни одна из сторон не особо победила, но и не особо проиграла - и в тоже время стопудово - что обе стороны выглядят достаточно глупо.


Во-первых, Twisted. Сейчас моя компания использует его за небольшую часть функциональности, потому что ТОГДА - это был самый простой способ, что мы нашли для отправки трафика через различные сетевые интерфейсы на Linux машинах. Мы никогда не имели с ним никаких проблем. Единственная причина, по которой мне необходимо его когда-нибудь тронуть - это чтобы увидеть, как что работает.

Тем не менее, Twisted, вероятно, самая запарная программная библиотека. Каждый раз, когда я открываю этот код, я чувствую, что я забрел в ночной бар на берегу Джерси, где все пьяные в хлам и уже давно сорвали свои рубашки. Twisted классная библиотека, но в тоже время - НЕДОСТАТОЧНО классная, чтобы действительно называться "Twisted". Это Python- программистская версия одежды и бейсболок Ed Hardy, которая все еще висит в стороне (модная вещь, которая не используется). Когда я копался в этом коде и мои коллеги спрашивают меня, что случилось, единственным адекватным ответом было я "НЕ СЕЙЧАС, шеф - я запускаю этот хренов реактор ".

Теперь вы можете понять, почему появилась и существует такая хрень, как Tornado.

Хотя я постоянно не рекомендую делать такие вещи как Tornado - Friendfeed все-таки ее сделали. Из тех графиков, которые я видел, Tornado просто незначительно быстрее, чем Twisted на обслуживании большого количества одновременных запросов. НЕЗНАЧИТЕЛЬНО. Очевидно, в Friendfeed полагали, что достаточно небольшая разница в скорости была достаточным основанием тратить свое время и что-то переписывать заново- что обычно и делает каждый разработчик, которому становится скучно на работе. Веб-фреймворк на Python? О боже - как это оригинально! Я думаю, что это один из последних уроков книжки "Изучи Python за 24 часа".

В Friendfeed потратили много времени, пытаясь оптимизировать количество запросов в секунду, отображаемого на графике, но, возможно, им следовало бы тратить больше времени на оптимизацию ВОТ ЭТОГО графика - вместо первого:

Во всяком случае, когда речь идет о выборе Twisted vs Торнадо для веб-фреймворка, я использую Django. Почему? Потому что это работает, и мое время ценно.

Достаточно вольный перевод вот этой заметки. Спасибо автору

Кстати, достаточно интересно пишет, хотя и многие слова приходится долго думать - перед тем как перевести на русский.

пятница, 9 декабря 2011 г.

HeadHunt: нужны Highload Python, AppManager и тестеры

Мы ищем несколько человек - специалистов по highload Python, один application manager и хороших тестировщиков в СПб.

"Мы" - это ООО «Стрим», дочерняя компания ОАО «МТС»

Разработчик платформенных решений ( Python, Highload)

Обязанности:

  • разработка и дальнейшее сопровождение серверной части медийных сервисов для проекта Омлет.ру (в первую очередь сервиса управления и распространения легального видео-контента);
  • консультирование по вопросам высоконагруженных систем.
Требования:
  • опыт разработки на Python от 2 лет;
  • хорошее знание SQL, опыт работы, а так же оптимизации и тюнинга СУБД PostgreSQL или MySQL;
  • опыт работы с no-SQL решениями,  в проектах по запуску высоконагруженных веб-сервисов (от 500 тыс показов в сутки);
  • аналитический склад ума, коммуникабельность, знание основ ООП, хорошая математическая база;
  • дополнительными плюсами являются: знание и опыт применения различных языков программирования, новейших технологий, самообучение и саморазвитие, английский язык, опыт создания несложных UI
Если будет практический опыт Tornado/Twisted - вам большой такой плюс.

Специалист по передаче в эксплуатацию (application manager)

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

Задачи:
  • написание скриптов деплоя приложений или настройка специализированной среды развертывания приложений;
  • приемка серверных приложений после тестирования;
  • передача приложения в эксплуатацию;
  • валидация документации по установке приложений;
  • написание документации по эксплуатации;
  • анализ поведения приложений при эксплуатации, выявление сложностей эксплуатации приложений; 
Требования к кандидату:
  • владение linux на уровне базового администрирования;
  • владение windows на уровне продвинутого пользователя;
  • опыт написания скриптов на shell;
  • знание одного из языков: php, ruby, python;
  • опыт написания документации;
  • аналитический склад ума;

Специалист по тестированию

Обязанности:
  • тестирование серверных компонент видео-сервиса и всего сервиса в целом, в том числе - автоматизация тестирования и нагрузочное тестирование
Требования:
  • образование в сфере ИТ, желательно понимание основ программирования;
  • опыт функционального и нагрузочного тестирования от 2 лет;
  • хорошее знание SQL;
  • опыт работы в проектах по запуску высоконагруженных веб-сервисов (от 500 тыс. показов в сутки);
  • аналитический склад ума, коммуникабельность, знание основ ООП;
  • владение инструментами JMeter или аналогичными;
  • дополнительными плюсами являются: знание и опыт применения различных языков программирования, знание английского языка.
Условия для всех вакансий:
  • вся белая ЗП, оформление по ТК;
  • ненормированный рабочий день;
  • 31 день оплачиваемого отпуска;
  • испытательный срок 3 мес.
  • ДМС после прохождения испытательного срока;
  • корпоративная мобильная связь;
  • квартальные и годовые бонусы;

Если ваше резюме удовлетворяет требованиям на 3/4 - смело шлите его на michael(dot)neradkov(dog)gmail(dot)com. 

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

Заранее спасибо!



воскресенье, 20 ноября 2011 г.

Как на python проверить, открыт ли какой-то port?


from socket import socket, gethostbyname, AF_INET, SOCK_STREAM

target = "localhost"
targetIP = gethostbyname(target)
port = 80
s = socket(AF_INET, SOCK_STREAM)

result = s.connect_ex((targetIP, port))

if(result == 0) :
 print 'Port %d is open' % (port,)
s.close()

понедельник, 14 ноября 2011 г.

Как реализовать периодические задачи в Twisted? (cron vs LoopingCall)

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

Есть же методически грамотное, ВСТРОЕННОЕ решение - и нашел я его сегодня случайно, когда искал совершенно другую вещь.

from twisted.internet.task import LoopingCall

#==============================================================================
class MySuperServer(MyServer, twisted.web.server.Site):
    '''
    мой собственный веб-сервер, с блекджеком и шлюхами ;-)
    '''

    def __init__(self):
        lp = LoopingCall(self.checkStatus)
        lp.start(1.0) # период в секундах    

    def checkStatus(self):
        #self._log('Looping call')
        pass

четверг, 3 ноября 2011 г.

Python, Twisted, logging - Как логгировать несколько процессов/демонов в один файл?

Давайте поговорим немного про логгирование в Python.

Да, меня это ДЕЙСТВИТЕЛЬНО беспокоит, и я хочу об этом поговорить ;-)

Совсем недавно столкнулся с тем, что достаточно серьезный проект при портировании с Linux на MacOS потерял логгирование. Ну нет лог файла - и все тут. Полез в код разбираться - выяснилось, что разработчики очень любили изобретать велосипеды и делали логгирование через файл, открытый ручками и обычную запись туда переменных - прямо в файл. Все это работало... до поры до времени. Пришлось на скору руку переписывать.

В общем, мораль сей басни такова...

Есть в Python СТАНДАРТНЫЙ модуль logging  - его и нужно использовать для разного типа логгирования. Батарейка-то уже включена ;-)

Чтобы пример кода не был совсем тривиальным - приведу решение для той ситуации, когда у нас есть несколько многопроцессных РАЗНЫХ демонов на Twisted на одной машине, а еще и cron-скрипт запускается время от времени. И всю жизнедеятельность этого дурдома нужно логгировать в один файл

import os
import logging
from twisted.python import log                                                                                   

CURRENT_PATH = os.path.realpath(os.path.dirname(__file__))

# Все это "по уму" обычно запихивается в нормальный конфиг-файл
LOG_DIR = os.path.join(CURRENT_PATH, 'logs')                            
LOG_FILE = os.path.join(LOG_DIR, 'super-puper.log')  
LOG_LEVEL = logging.INFO
 
# инициализируем
FORMAT = ('%(asctime)-15s %(levelname)s %(message)s') 

if not os.path.exists(LOG_DIR):
    os.mkdir(LOG_DIR) 
 
logging.basicConfig(format=FORMAT, filename=LOG_FILE, handler=logging.handlers.RotatingFileHandler)   
observer = log.PythonLoggingObserver(loggerName='twisted')
observer.start()
observer.logger.setLevel(LOG_LEVEL)  

# класс-примесь для логгирования - в принципе - необязательно, реализация может быть разная - хоть функциями
class BasicLogClass():

    def _log(self, msg):
        log.msg(msg, logLevel=logging.INFO)

    def _error(self, msg):
        log.msg(msg, logLevel=logging.ERROR)

    def _warning(self, msg):
        log.msg(msg, logLevel=logging.WARNING)

В данном варианте работоспособность не проверял, но логика применения именно такая - выкусил из работающего проекта.

Кстати, если у кроновских скриптов нет использования twisted'овского reactor, то могуть быть проблемы, нужно подумать. Как вариант - использовать развилку в инициализации и логгировании без применения observer, но не уверен.