23.01.10

Python debug-progressbar

Поддержу тему прогресс-баров начатую на Хабре. Описанный там бар можно назвать "продакшн-баром". В свою очередь, поделюсь давно и удачно написанным "дебаг-баром". Основные его отличия в:

  • возможности вывода дополнительной дебаг-инфы
  • выводе времени обработки каждого шага
  • поддержке стандартного питоньего logging-а

Используется он так:

from random import randint
from debugbar import cnt

data = range(10)  # некоторые данные для обработки

cnt = Cnt(len(data), 2)  # указываем размер данных и шаг счётчика
for d in data:
    cnt.put('дополнительная инфа %s' % d)  # выводим бар
    time.sleep(randint(1, 3))

В результате получится что-то типа:

 2 / 10 |  1.00 s. | дополнительная инфа 1
 4 / 10 |  5.00 s. | дополнительная инфа 3
 6 / 10 |  4.00 s. | дополнительная инфа 5
 8 / 10 |  3.00 s. | дополнительная инфа 7
10 / 10 |  4.00 s. | дополнительная инфа 9

Вариант с использованием логгинга:

import logging
from random import randint
from debugbar import cnt

log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG,
    format='%(levelname)-8s %(message)s')

data = range(10)  # некоторые данные для обработки

cnt = Cnt(len(data), 2)
for d in data:
    cnt.put('дополнительная инфа %s' % d, log.info)
    time.sleep(randint(1, 3))

Результат:

INFO      2 / 10 |  2.00 s. | дополнительная инфа 1
INFO      4 / 10 |  4.00 s. | дополнительная инфа 3
INFO      6 / 10 |  4.00 s. | дополнительная инфа 5
INFO      8 / 10 |  3.00 s. | дополнительная инфа 7
INFO     10 / 10 |  2.00 s. | дополнительная инфа 9

Ну и код самого модуля debugbar.py:

# -*- coding: utf-8 -*-
import sys
import time
from datetime import datetime, timedelta    


class T:
    '''Таймер'''
    def __init__(self):
        self.start = time.time()

    def __str__(self):
        sec = int(time.time() - self.start)
        return '%2.2f s.' % sec if sec < 60 else str(timedelta(seconds=sec))


class Cnt:
    '''Счётчик'''
    timer_tpl = ' | %8s'

    def __init__(self, limit, step=None, start=1, timer=True):
        self.limit = limit
        self.step = step if step else int(limit / 100) or 1
        self.cnt = start
        self.timer = timer
        self.cnt_tpl = '%%%(cnt_len)si / %%%(cnt_len)si' % {
            'cnt_len' : len(str(limit))}
        if timer:
            self.tm = T()

    def put(self, msg='', log=None):
        if not self.cnt % self.step or self.cnt == self.limit:
            m = self.cnt_tpl % (self.cnt, self.limit)
            if self.timer:
                m += self.timer_tpl % self.tm
                self.tm = T()
            if msg:
                m += ' | %s' % msg
            if log:
                log(m)
            else:
                print m
            sys.stdout.flush()
        if self.cnt < self.limit:
            self.cnt += 1