Tag Archives: python

Легенда про самодокументований код

Якось був код:

        if self.get_package_ids() == [None]: # only free package

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

    def only_free_package(self):
        return self.get_package_ids() == [None]

І коментар в нашому рядочку став зайвим:

        if self.only_free_package():

Ось і все. Це коротка легенда з простим сюжетом. Її мораль – методи призначені не тільки для повторного використання. Їх також варто використовувати для ізоляції рівнів абстракції.

Макконел також розповідає що можна писати спочатку псевдокод, потім його поступово закоментовувати (якщо він хороший він може бути документацією), і під ним писати решту коду.

Але дуже часто я бачив псевдокод нижчого рівня ніж Python. Я читав погані псевдокоди?


Filed under: Кодерство Tagged: паттерни, розробка, Python

Інтроспектор: “Хто присвоїв атрибуту це значення?”

Ану признавайтесь падлюки! :)

Іноді в процесі зневадження з’являється питання “Хто й коли присвоює цьому атрибуту неправильне значення?”. Покрокове проходження через тисячі рядків коду нудно і занадто складно. Що робити?

Елементарно, Ватсон! В Python надзвичайна здатність до інтроспекції.

Візьмімо для прикладу таку програму:

from watch_attribute import watched_attribute
import random

def change_here(x):
    x.x = random.random()

class Foo(object):
    add_watched_attribute('x')

foo = Foo()
foo.x = 2

for i in range(2):
    foo.x = foo.x * 2
    change_here(foo)

y = foo.x

Програма притягнута за вуха, суто щоб потестувати watched_attribute – функцію недавно написана мною, яка змушує атрибут доповідати про всі дії які з ним роблять. Результат роботи програми виглядає наприклад так:

/home/bunyk/progs/test_watch_attribute.py:13 foo.x = 2
x set to 2
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x is 2
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x set to 4
/home/bunyk/progs/test_watch_attribute.py:7     x.x = random.random()
x set to 0.288351784619
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x is 0.288351784619
/home/bunyk/progs/test_watch_attribute.py:16     foo.x = foo.x * 2
x set to 0.576703569239
/home/bunyk/progs/test_watch_attribute.py:7     x.x = random.random()
x set to 0.300510362936
/home/bunyk/progs/test_watch_attribute.py:19 y = foo.x
x is 0.300510362936

З прикладу незрозуміло нащо це треба, тому я покажу як я використав це на роботі. Отак:

('uaprom/forms/wt_fields.py', 1583) 'sets data to' 90000L
('wtforms/form.py', 133) 'gets data' 90000L
('wtforms/form.py', 133) 'gets data' 90000L
('uaprom/forms/wt_fields.py', 1583) 'sets data to' 90000L
('uaprom/forms/wt_fields.py', 1596) 'sets data to' 501L
('uaprom/forms/wt_fields.py', 1634) 'gets data' 501L
('uaprom/forms/wt_fields.py', 1635) 'gets data' 501L
('uaprom/forms/wt_fields.py', 1635) 'sets data to' 9018.0
('uaprom/forms/wt_forms.py', 931) 'gets data' 9018.0
('uaprom/forms/wt_validators.py', 721) 'gets data' 9018.0
('uaprom/forms/wt_validators.py', 722) 'gets data' 9018.0
('uaprom/forms/wt_validators.py', 722) 'sets data to' 501.0 <-- от хто винен!
('wtforms/validators.py', 150) 'gets data' 501.0
('uaprom/forms/wt_fields.py', 1629) 'gets data' 501.0

Звичайно треба ламати руки тому хто змінює значення полів в валідаторах, але я живим не дамся! Приклад нам показує що коли атрибут читають і пишуть 14 разів в 5 різних файлах, то перечитування коду чи навіть трасування програми не допоможе знайти проблему так швидко як такий лог. Бо поки залізеш в один файл, розпечатаєш там значення, поки в інший…

Ну, а зараз час подарувати вам код модуля watched_attribute:

import sys
import inspect

def from_where_called():
    info = inspect.getframeinfo(sys._getframe(2))
    # інформація про фрейм на два вище за поточний
    # (функція яка викликала функцію що викликала оцю функцію)
    code = info.code_context[0] if info.code_context else ''
    return '%s:%s %s' % (info.filename, info.lineno, code)

def add_watched_attribute(name):  
    def attr_watch_get(self):
        value = getattr(self, '_' + name, 'unset')
        print from_where_called(), name, 'is', value
        return value

    def attr_watch_set(self, value):
        print from_where_called(), name, 'set to', value
        setattr(self, '_' + name, value)

    sys._getframe(1).f_locals[name] = property(attr_watch_get, attr_watch_set)

Від чого я в цьому коді прусь, так це від того, що getter і setter для property сховані в замиканні, і не засмічують простір імен класу. А тепер також від того, що функція виявляється має доступ на запис, до простору імен на рівень вище (останній рядок). Ну хіба це не прекрасно?


Filed under: Кодерство Tagged: зневадження, Python

Найпростіший спосіб вимірювання найгіршої метрики

Або іншими словами – примітивний облік робочого часу в лінуксах.

Для цього є команда – last. Дозволяє подивитись в логах час останніх входів і виходів з системи. Якщо наприклад дати команду

last 7 # еквівалентно last tty7

Дізнаємось хто коли і скільки користувався сьомим терміналом:

bunyk    tty7         :0               Thu Feb  2 08:30 - down   (09:32)

Вдома попробував – не працює. А виявляється все тому, що є певна конфігурація ротації логів /etc/logrotate.conf, в якій написано – monthly.

Але команду я дізнався на початку місяця, коли ще жодних логів не було навіть на роботі. Якось неправильно ця ротація працює. Вдома логи чомусь все одно ведуться за один день, хоча я й написав що хочу місячну ротацію. Може не в ті логи написав.

А ще збацав коротенький скриптик, який бере дані з last, і рахує середній робочий час:

#!/usr/bin/python3
import re
import subprocess 

def hours(s):
    s = s.split(':')
    return float(s[0]) + float(s[1]) / 60

def to_str(x):
    h = int(x)
    m = 60 * (x - h)
    return '%s:%s' % (h, int(m))

output = subprocess.check_output(['last', '7']).decode('utf-8')

data = (re.findall(r'\((\d\d:\d\d)\)', x) for x in output.splitlines())
data = [hours(x[0]) for x in data if x]

print(output)
print('Average time: ', to_str(sum(data)/len(data)))

bunyk@bunyk-laptop:~/workspace/uaprom$ /home/bunyk/experimental/timestat.py 
bunyk    tty7         :0               Thu Mar 22 09:32   still logged in   
bunyk    tty7         :0               Wed Mar 21 10:37 - 19:51  (09:13)    
bunyk    tty7         :0               Tue Mar 20 11:48 - down   (08:16)    
bunyk    tty7         :0               Mon Mar 19 11:38 - down   (07:14)    
bunyk    tty7         :0               Sat Mar 17 09:45 - down   (08:36)    
bunyk    tty7         :0               Fri Mar 16 10:35 - 19:23  (08:48)    
bunyk    tty7         :0               Thu Mar 15 10:22 - down   (08:47)    
bunyk    tty7         :0               Tue Mar 13 10:02 - 19:44  (09:42)    
bunyk    tty7         :0               Mon Mar 12 12:08 - 20:34  (08:25)    
bunyk    tty7         :0               Wed Mar  7 10:29 - down   (07:42)    
bunyk    tty7         :0               Tue Mar  6 09:54 - down   (09:50)    
bunyk    tty7         :0               Mon Mar  5 11:15 - 19:32  (08:16)    
bunyk    tty7         :0               Sat Mar  3 11:21 - down   (07:03)    
bunyk    tty7         :0               Fri Mar  2 12:08 - down   (08:00)    

wtmp begins Thu Mar  1 18:46:07 2012

Average time:  8:27

Виходить що я трохи більше ніж пів-години на добу недопрацьовую. Правда шпигунський роман Тоні ДеМарко розповідає про те що до програмістів краще не застосовувати метрики, бо ті хитрі зарази знайдуть спосіб максимізувати метрику не покращуючи продукт. Якою гарною ця метрика не була б (хай навіть зміною відношення кількості вад до кількості функціоналу). А Тоні ДеМарко – виявляється в вікіпедії стоїть поруч з такими гігантами як Вард Каннінгам, Мартін Фаулер і навіть Тоні Хоар. Напевне варто познайомитись детальніше. Правда часу нема. Але якщо він не важливий, тоді важливе лише те що я вважаю важливою дією в кожен момент часу.


Filed under: Інструменти, Кодерство Tagged: linux, робота, Python

Кібернетична вікі. Епізод II. Атака ботів

Не так давно
на одному
сервері кібцентру…

В Кібернетичній вікі
неспокійно. Більше сотні
користувачів демонструють
наміри закидати її спамом.

Рух спамерів очолюваний
загадковим невідомо ким
(чи взагалі ніким не очолюваний),
перешкоджає малочисельним кіберджедаям
зберігати чистоту й порядок на сайті.

Адміністрато bunyk повертається в КібВікі
щоб зайнятись принциповим питанням: створенням
бота для допомоги джедаям в малоуспішній боротьбі…

Пафосні титри подивились – переходим до банального сюжету про те як добро перемогло зло. :) Зі мною була нова сила (Python 3), і розуміння того що об’єктно-орієнтоване програмування призначене для зменшення кількості подібних параметрів в різних методах. І знання PEP8 яке дозволяє писати код на 8 та більше балів за Пайлінтом.

До того дії всіх ботів я відкидав вручну, слідкуючи за вікі через RSS. Коли їх було 1-2 на день це ще можна було терпіти. Але коли вони повалили десятками я на деякий час забив.

Я вирішив не морочити собі голову з PywikipediaБотом, для роботи якого на cybwiki ще треба створювати окремий файл конфігурації, а змайструвати свого. Свого писати набагато приємніше ніж лазити по сотнях файлів чужого. Крім того, мені дуже соромно за старий bunykwikibot, бо той код – блювотний. Зрозуміло що рік тому я цього не міг бачити. Тому маючи ще один день відпустки який залишився з проходження практики вирішив використати цей шанс щоб виправити ситуацію.

Знайомтесь – bunykwikibot2. Поки що різноманітних запитів до вікі в нього менше. Але зате він вміє логінитись і писати. І з ним приємно працювати.

Покажу на прикладі сьогоднішнього бою проти спамерів (ввесь код):

#!/usr/bin/python3

Ага. Рухаємось вперед. Бо якщо не ми, то хто?

from api import Wiki

Простіше простого. І не треба завантажувати штук п’ять функцій!

def hunt_bots():
    wiki = Wiki('http://cybportal.univ.kiev.ua/w/api.php')
    wiki.session_file('bunyk_on_cybwiki.session')

Це в нашого скрипта головна функція. При запуску створює новий клієнт wiki, для API за заданою адресою. Другий рядочок каже що дані сесії (куки і ще дещо) будуть завантажуватись з вказаного файлу. А якщо їх там нема – попросити в користувача залогінитись і записати туди дані сесії.

    def marked_bot(user):
        userpage = user.userpage().read() or ''
        return '{{Spambot}}' in userpage

Як розрізнити спам і не спам? Пол Грем написав статтю про Баєсівський класифікатор, а я як останній неуч її все ще не читав. Тому вирішив просто позначити всіх ботів вручну, шаблончиком {{Spambot}}. Функція вище отримує текст сторінки користувача, і дивиться чи там не написано що користувач – бот.

Зрозуміло бота ще треба позначити. Цим займається наступна функція:

    def shoot_bot(bot):
        bot.userpage().write('{{Spambot}}', 'Попався!')

Перший її рядок замінює текст сторінки бота позначкою про те що він бот, супроводжуючи цю дію бойовим криком “Попався!”.

        for contrib in bot.contributions():
            spam_page = wiki.page(contrib['title'])
            only_bots = True
            for user in spam_page.contributors():
                if not marked_bot(user):
                    only_bots = False
                    break
            if only_bots:
                spam_page.delete('Спам.')

Далі по черзі розглядаються всі сторінки які бот редагував. І якщо єдиними редакторами цих сторінок були вже позначені боти – сторінки теж видаляються.

        bot.block('Die spammers, die!')

Після цього бот блокується з іншим грізним криком.

Але основна частина скрипта – визначення хто бот, а хто ні:

    for user in wiki.all_users:
        userpage = user.userpage()
        page_text = userpage.read()
        if page_text:
            if '{{Spambot}}' in page_text:
                print(user.name, '- ботяра')
                continue
            if '{{Approved}}' in page_text:
                print(user.name, '- чесний')
                continue
            print(page_text)
        else:
            print('Сторінка користувача порожня')
        edits = user.edit_count()
        print(userpage.title, 'Редагувань: %d' % edits)

Я розглядав кожного користувача. Якщо він вже позначений – виводив повідомлення і брав наступного. Якщо ж ні – виводив текст його сторінки користувача, логін і кількість редагувань.

        if edits < 3:
            verdict = True
        else:
            verdict = ask_y_n('Це бот, як думаєте?'
                '(y/n або порожній рядок якщо не знаєте)'
            )

Спочатку кожну справу я розглядав вручну. Потім до мене якось дійшло що кожен бот залогінювався і робив не більше двох редагувань. Видно вони на більше не розраховані бо їх банять і доводиться реєструвати нові аккаунти. Правда через це мало не “постраждав” Номіровський тому що статтю про себе він редагував переважно анонімно, але я все виправив руками.

        if verdict == '':
            continue
        elif verdict:
            shoot_bot(user)
        else:
            userpage.append(
                '{{Approved}}',
                'Підтвердження того що власник сторінки - не бот.'
            )

Ну і коли сторону користувача визначено, він або розстрілювався, або позначався як наш. Якщо не визначено – пропускався.

Ну після довгої підготовки бій був коротким:
Робота бота

Результати: заблоковано 114 ботів. 40 нормальних користувачів підтверджено.

Тепер напевне пора думати що робити з наступними навалами? Банити всіх хто не поставить на свою сторінку відповідний шаблон, а анонімів за географічною ознакою? Чи таки вчити баєсівський класифікатор розрізняти дифи? Побачимо.


Filed under: Кодерство, Павутина Tagged: CybWiki, вікіпедія, Python

Небезпечні змінні об’єкти як значення аргументів функції за замовчуванням

Якось пишу я один сайт (та все той же) і натикаюсь на отаку грізну помилку:

Через деякий час знайшлась і причина помилки, і те падло яке цю причину написало:

Bunyk Fri Oct 07 13:45 2011:   def __init__(self, label=u'', validators=[
Bunyk Fri Oct 07 18:31 2011:       validators.NumberRange(min=decimal.Decimal('0.01'),
Bunyk Fri Oct 07 13:45 2011:       message=_(u"Значение должно быть больше чем %(min)s"))
Bunyk Fri Oct 07 13:45 2011:       ], **kwargs
Bunyk Fri Oct 07 13:45 2011:   ):

Ну справді, хто так пише. Дужку варто закривати на тому ж рівні вкладеності на якому вона відкривалась це раз:

def __init__(self, label=u'',
    validators=[validators.NumberRange(
        min=decimal.Decimal('0.01'),
        message=_(u"Значение должно быть больше чем %(min)s")
    )],
    **kwargs
):

І два – списки, словники та інші змінні (mutable) об’єкти не надто бажані як значення аргументів за замовчуванням.

Чому так? Тому що це уможливлює ефекти про які я зараз розповім. А ці ефекти можуть здивувати, як і повідомлення на картинці.

Як відомо, в Python все є об’єктом. Навіть функції і класи. А про будь-який об’єкт найважливіше що варто знати – це коли і як він створюється.

Тепер давайте розглянемо який-небудь сферичний код в вакуумі. Наприклад такий:

def next(line=[]):
    line.append(len(line) + 1)
    print line
    
next()
# OUT: [1]
next()
# OUT: [1, 2]
next()
# OUT: [1, 2, 3]
next(range(10))
# OUT: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]
next()
# OUT: [1, 2, 3, 4]
next(line=[])
# OUT: [1]
next()
# OUT: [1, 2, 3, 4, 5]

Так от, щоб зрозуміти чому він так поводиться, варто знати, що змінні (і аргументи функцій) в Python це насправді не значення а посилання. А [] – конструктор порожнього списку.

В першому рядку коду створюється об’єкт функції, разом з ним створюється об’єкт списку, і стандартним значенням параметра функції призначається посилання на цей список.

Можливий варіант переписування коду так щоб він працював більш “очікувано”:

def next(line=None):
    if not line:
        line = []
    line.append(len(line) + 1)
    print line

Ну а зараз строгостатичні чистолямбдасамці можуть срати цеглою нижче. ;)


Filed under: Кодерство Tagged: помилки, робота, Python

Тонке налаштування Buildbot’а

No Gravatar

Сьогодні ми продовжимо налаштування Buildbot’а. Сподіваюсь, читач вже знайомий з попереднім матеріалом на дану тему, тому тут будуть вказані типові запитання, вдосконалення, можливі проблеми без зайвих слів.

Read more »

Автоматизація процесу розробки: працюємо з Buildbot’ом

No Gravatar

Як я й обіцяв в минулій статті, сьогодні ми дослідимо реальний приклад використання системи неперервної інтеграції під назвою BuildBot. Ні сам процес розробки, ні структура проекту не вважаються ідеальними, однак саме завдяки їм у мене зараз є можливість написати як можливо вижити в, здавалося б, такому хаотичному проекті.

Постановка задачі

Проект на Java(використовується система збирання Ant) в репозитарії Subversion знаходиться за адресою https://project.example.com/svn . Необхідно проводити аудит працездатності системи щоразу після внесення змін до нього. Результати збирання системи надсилаються автору змін у випадку успішного результату, або ж у список розсилки всього проекту, якщо процес пройшов невдало.

Вибір між CruiseControl, про який мені розповіли співробітники, та BuildBot, з яким я встиг познайомитись в якості розробника, був досить складний. Проект був вже тоді немаленький(на даний момент весь репозитарій складає 6Гб місця), після тестування його переносила на продуктивні сервери окрема людина. Enterprise, одним словом. І я, довірившись особистому знайомству, вибрав BuildBot. І не прогадав.

Read more »

Автоматизація процесу розробки: системи неперервної інтеграції

No Gravatar

Сьогодні, шановний читачу, ми продовжимо розмову про те, як можна автоматизувати процес розробки програмного забезпечення, зменшити витрати часу на допоміжні роботи в великому колективі і сконцентруватись на власне розробці.

У колективі, кожен член якого працює над окремою незалежною частиною, стадія інтеграції є заключною. Саме в ній виявляються проблеми сумісності всіх компонентів системи, через це кінцева дата випуску може бути відсунута на невизначений термін. Саме на зменшення часових та трудових витрат на процес інтеграції за рахунок раннього виявлення проблем та усунення помилок та протиріч спрямовані системи неперервної інтеграції.

Read more »

Управління проектами з Trac

No Gravatar

Задача: отримати багатоцільове open-source середовище для управління проектами з web-інтерфейсом, підтримкою Git та БД PostgreSQL, автентифікацією через LDAP.

Обираємо систему управління проектами

За інформацією для роздумів гугл нас привів на сторінку з вікіпедії. Як бачимо, вибір у нас досить великий. Вибрати з них що-небудь по собі з виконанням більшості критеріїв досить нескладно. Однак підтримку Git має в своїй реалізації лиш один проект — Trac, про який і йтиме мова далі.

Чому Trac

Trac logo

Версія 0.11
Мова Python
Сайт http://trac.edgewall.org/
Ліцензія Модифікована ліцензія BSD

Як вже було сказано, лише в нього я знайшов підтримку Git репозиторіїв. До того ж Trac має неабияку гнучкість: окрім базового набору, якого зазвичай не вистачає, купа функціоналу реалізована у вигляді плагінів, яких тут можна знайти на будь-який смак. Простий та зрозумілий з першого погляду інтерфейс, відсутність зайвих “свистілок” не може не порадувати людей, які від продукту очікують ефективну реалізацію функціоналу. Можливість адаптувати середовище на власний смак та за потребами проекту, будь то розробка програмного забезпечення, адміністрування, чи організація господарських робіт у себе на городі. Хоча незважаючи на можливість вести будь-які проекти в цій системі, все ж вона тісно інтегрована з ситемами контролю версій(Svn, Bazaar, Mercurial, Git), підсвіткою синтаксису “на льоту”(використовуються SilverLight, Enscript, а з версії 0.11 — ще й Pygments), розробкою документації (Doxygen, PyDoc, PerlDoc), і навіть системою управління тестами. Підтримка RSS-стрічок, wiki(до якої є теж купа розширень), і навіть ведення блогу додають практично всі необхідні інструменти для ведення сучасного проекту. Довершує все це добро документація по Trac, яка є в кожному новоствореному проекті на стоірнках wiki.

Установка та адміністрування

Ставити ми його будемо, звичайно ж, на Debian GNU/Linux, для якого поточною стабільною віткою вважається 0.10. Сподіваюсь, ви вже маєте налаштований Git репозиторій, інакше раджу взяти низький старт з цієї сторінки. Виконавши aptitude install apache2 libapache2-mod-python trac-core trac-git pcycopg2 та встановивши залежності й додаткові пакети за бажанням переходимо власне до конфігурації.

Першим кроком буде створення нового проекту. Де тримати проекти — справа кожного, хоча мені більше імпонує покласти їх до /var/trac/. Для промислового розгортання буде доцільним розгортати подібні середовища в домашніх директоріях користувачів. Отже, почнемо.

# mkdir /var/trac
# cd /var/trac
# trac-admin myproject initenv

В процесі установки система необхідно ввести деякі базові настройки, вибрати БД, з якою працюватимете, та вказати шлях до репозиторію. Якщо в якості репозиторію ви вказали Git, то система в кінці видасть попередження про відсутність його підтримки, однак все це можна сміливо ігнорувати, бо все це ми виправимо в подальших кроках.

trac-admin management utilityСаме з цього моменту починають проявлятись особливості роботи з Trac: адміністрування проекту проводиться за допомогою консольної утиліти trac-admin. Отже, якщо ви не маєте доступу через SSH до свого сервера, годі й мріяти про установку цієї системи. І хоча деякі операції можливо перекласти на веб-морду, однак початкову ініціалізацію — зась. Якщо ж ви все ще з нами і вже готові до наступного кроку, то не гаймо часу.

Всі налаштування проекта знаходяться в одному файлі conf/trac.ini в директорії проекту. Зараз можна підправити загальні параметри проекту, однак я раджу відкласти цей крок на момент після тестового запуску проекту. Справа в тому, що будь-який згенерований проект з самого початку містить wiki-документацію, яка доступною мовою опише можливості та особливості налаштування системи. Знову повертаючись до Git, на сторінці GitPlugin ви зможете прочитати вичерпні пояснення щодо того, яким чином організувати співпрацю Trac та Git.

Для успішного старту проекту необхідно отримати можливість запускати Python-івські скрипти на запит веб-сервера. Це можна реалізувати через механізм CGI, FastCGI, або ж використовуючи mod_python, що ми, власне, і зробимо:

<Location/projects>
  SetHandler mod_python
  PythonInterpreter main_interpreter
  PythonHandler trac.web.modpython_frontend
  PythonOption TracEnvParentDir /var/trac
  PythonOption TracUriRoot /projects
</Location>

Вище представлений шматок конфігураційного файлу веб-сервера, який дозволить вести нам безліч проектів, що лежатимуть в /var/trac Наступні рядки ж дозволять мати один обліковий запис для всіх проектів. Даний тип автентифікації приведений лише для прикладу. Автентифікація користувачів через систему LDAP буде описана в наступних статтях.

<LocationMatch"/projects/[^/]+/login">
  AuthType Basic
  AuthName "Trac"
  AuthUserFile /var/trac/.htpasswd
  Require valid-user
</LocationMatch>

Якщо всі кроки виконані успішно, ви мусите побачити вже працюючий проект за адресою:http://localhost/projects/myproject/

Додаткові плагіни

Деякі з плагінів потребують особливої уваги при роботі з ними. Тут розглянуті лиш два з них, хоча насправді проблеми можуть виникнути в багатьох плагінах, функціонал яких пов’язаний з іншими системами, зокрема плагіни, що реалізують автентифікацію.

GraphvizPlugin

Trac with GraphViz pluginТрохи помучитись змусив мене плагін, що реалізовує inline-вставку графічного представлення інформації за допомогою graphviz. Для тих, хто все ще не може отримати зображення, підкажу основні вимоги:

  • директорія, в якій знаходиться кеш graphviz повинна бути доступна для запису користувачем www-data, під яким запущено веб-сервер. Крім того, якщо ви використовуєте tmpfs, то не робіть директорію для кешу в /tmp, оскільки для роботи graphviz необхідне існування директорії, а при наступному ж перезавантаженні вона просто зникне.
  • необхідні пакети: graphviz та librsvg2-bin
WebAdmin

Для того, щоб не мати клопоту при роботі з цим плагіном, необхідно, щоб файли проекту в /var/trac/myproject були доступними користувачеві www-data для запису. Загалом же, плагін встановлюється та підключається без зайвих зусиль.

Trac та PostgreSQL 8.3

Якщо ви використовуєте Trac 0.10 зі стандартних пакетів Debian, то матимете клопіт з неможливістю перегляду звітів про помилки (tickets). При цьому ще й при додаванні такого звіту система може видавати помилки. Для того, щоб усунути цю помилку, необхідно змінити у файлі /usr/share/python-support/trac/trac/ticket/model.py рядок 296 з:

"ORDER BY time", (self.id, str(self.id), self.id))

на

"ORDER BY time", (self.id, str(self.id), str(self.id)))

Сподіваюсь, що за кілька днів після випуску даної статті ця порада стане непотрібною ;)

Висновки

Закінчуючи статтю, хочеться підбити підсумки щодо системи в цілому. Особисто я вже використовую Trac для власних проектів, тому що ця система:

  • проста, зручна, та ефективна
  • має підтримку сучасних репозитаріїв, баз данних та прикладного програмного забезпечення
  • дуже гнучка
  • знаходиться в активній розробці

Хочете переконатися в цьому самостійно? Тоді ласкаво просимо до http://www.hosted-projects.com/trac/TracDemo/Demo , де ви зможете попрацювати з Trac без зайвого клопоту.

Серед недоліків даної системи щодо нашого ринку можу віднести майже відсутній хостинг з підтримкою Python та неможливість організації повного циклу життя проекту від його зародження і до закриття через веб-інтерфейс, хоча останнє скоро буде виправлене.

Exception 2008, або “Краще день загубити, потім за 5 хвилин долетіти”

No Gravatar

Саме під таким (на мою думку) девізом успішно відбулась конференція Exception #07, присвячена динамічним мовам програмування. Все почалося, коли я отримав листа з повідомленням “Завтра відбудеться Exception Conference #07″, де поміж всього іншого проскочила кількість зареєстрованих осіб: 389(в дійсності ж прийшло десь 300). “Ого, скільки народу!”, — подумав я, і вже з нетерпінням почав очікувати завтрашній день. Прочитавши інформацію про проведення минулорічної конференції та задоволення організацією, я був трішки вражений “натовпом”, який зустрів мене ще до реєстрації. Мабуть, організатори просто не очікували такого ажіотажу, що і не дивно: минулого року (судячи з інфи в блогах) було десь сто чоловік. Повний перелік виступів, а також коротка інформація про авторів знаходиться на офіційному сайті конференції.
Read more »