Skip to content

Commit

Permalink
initial revision
Browse files Browse the repository at this point in the history
  • Loading branch information
mbreese committed Oct 30, 2012
0 parents commit 3bb011d
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 0 deletions.
37 changes: 37 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Copyright (c) 2010-2012 The Trustees of Indiana University

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer listed
in this license in the documentation and/or other materials
provided with the distribution.

- Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

The copyright holders provide no reassurances that the source code
provided does not infringe any patent, copyright, or any other
intellectual property rights of third parties. The copyright holders
disclaim any liability to any recipient for claims brought against
recipient by any third party for infringement of that parties
intellectual property rights.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ETA
===

This is a python package that will create progress bars for command-line programs.

Example usage:
from eta import ETA
eta = ETA(ticks)
for foo in bar:
eta.print_status()
eta.done()

fobj = open(fname)
eta = ETA(os.stat(fname).st_size, fileobj=fobj)

for line in fobj:
eta.print_status(extra="extra message")
...
eta.done()
235 changes: 235 additions & 0 deletions eta/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
'''
Displays a progress meter and an ETA calculation based upon either a user supplied number (out of a total)
or based upon a file and it's size. In the case of a file, the position information is calculated from the
tell() method. The ETA is calculated by taking the average of the last 50 ETA calculations so that the numbers
can be smoothed out. Additionally, you can set a 'modulo' parameter that will only display a message every
N iterations (thus relieving you from having to calculate it).
Marcus R Breese <[email protected]>
Created: Jan 2010
Last update: Oct 2012
'''
import sys
import datetime
import os


def eta_open_iter(fname, callback=None):
f = open(fname) # not using with to support 2.4
_eta = ETA(os.stat(fname).st_size, fileobj=f)
for line in f:
if callback:
extra = callback()
_eta.print_status(extra=extra)
yield line
_eta.done()
f.close()


class _NoopETA(object):
def __init__(self, *args, **kwargs):
pass

def done(self):
pass

def print_status(self, *args, **kwargs):
pass


class _ETA(object):
def __init__(self, total, modulo=None, fileobj=None, window=50, step=1, prog_bar_length=20, min_ms_between_updates=200):
self.started = datetime.datetime.now()
self.last = []
self.total = total
self.spinner = "|/-\\"
self.spinner_pos = 0
self.i = 0
self.modulo = modulo

try:
fileobj.fileobj.tell()
self.fileobj = fileobj.fileobj
except:
self.fileobj = fileobj

self.last_len = 0
self.step = step
self.last_step = 0
self.window = window
self.prog_bar_length = prog_bar_length
self.min_ms_between_updates = min_ms_between_updates # in milliseconds
self._last_update = 0
self._started = 0

def pct(self, current):
if current < self.total:
return float(current) / self.total
return 1

def ave_remaining(self, current):
if len(self.last) > self.window:
self.last = self.last[-self.window:]
rem = self.remaining(current)
if rem:
self.last.append(rem)

acc = 0.0
for p in self.last:
acc += p

if len(self.last) > 0:
return acc / len(self.last)
else:
return None

def remaining(self, current):
elapsed = (datetime.datetime.now() - self.started).seconds
pct = self.pct(current)
if pct > 0:
eta = elapsed / self.pct(current)
else:
return None

remaining = eta - elapsed
return remaining

def pretty_time(self, secs):
if secs is None:
return ""

if secs > 60:
mins = secs / 60
secs = secs % 60
if mins > 60:
hours = mins / 60
mins = mins % 60
else:
hours = 0
else:
mins = 0
hours = 0

if hours:
s = "%d:%02d:%02d" % (hours, mins, secs)
elif mins:
s = "%d:%02d" % (mins, secs)
else:
s = "0:%02d" % secs

return s

def done(self, overwrite=True):
if overwrite:
sys.stderr.write('\r')
sys.stderr.write(' ' * self.last_len)
sys.stderr.write('\b' * self.last_len)

elapsed = (datetime.datetime.now() - self.started).seconds
sys.stderr.write("Done! (%s)\n" % self.pretty_time(elapsed))
sys.stderr.flush()

def print_status(self, current=None, extra='', overwrite=True):
self.i += 1
if self.modulo and self.i % self.modulo > 0:
return

if not self._started:
self._started = datetime.datetime.now()
elapsed_sec = 0
else:
elapsed_sec = (datetime.datetime.now() - self.started).seconds

if self._last_update:
elapsed = (datetime.datetime.now() - self._last_update)
millis = (elapsed.seconds * 1000) + (elapsed.microseconds / 1000)
if millis < self.min_ms_between_updates:
return

self._last_update = datetime.datetime.now()

if current is None:
if self.fileobj:
current = self.fileobj.tell()
else:
current = self.last_step + self.step

self.last_step = current

if overwrite:
sys.stderr.write("\r")
if self.last_len:
sys.stderr.write(' ' * self.last_len)
sys.stderr.write("\r")

if extra:
extra = " | %s" % extra

if self.prog_bar_length > 0:
pct_current = self.pct(current)
completed = int(self.prog_bar_length * pct_current)
remaining = self.prog_bar_length - completed
prog_bar = '[%s>%s] ' % ('=' * completed, ' ' * (remaining - 1))
else:
prog_bar = ''

line = "%6.1f%% %s %s %sETA: %s%s" % (pct_current * 100,
self.spinner[self.spinner_pos],
self.pretty_time(elapsed_sec),
prog_bar,
self.pretty_time(self.ave_remaining(current)),
extra)
width, height = getTerminalSize()
if len(line) > width:
line = line[:width]
sys.stderr.write(line)

if not overwrite:
sys.stderr.write('\n')
else:
self.last_len = len(line)

self.spinner_pos += 1
if self.spinner_pos > 3:
self.spinner_pos = 0
sys.stderr.flush()

#
# getTerminalSize from StackOverflow:
# http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python


def getTerminalSize():
def ioctl_GWINSZ(fd):
try:
import fcntl
import termios
import struct
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
'1234'))
except:
return None
return cr
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)

if not cr:
try:
fd = os.open(os.ctermid(), os.O_RDONLY)
cr = ioctl_GWINSZ(fd)
os.close(fd)
except:
pass

if not cr:
try:
cr = (os.environ['LINES'], os.environ['COLUMNS'])
except:
cr = (25, 80)

return int(cr[1]), int(cr[0])

if 'HIDE_ETA' in os.environ:
ETA = _NoopETA
else:
ETA = _ETA
13 changes: 13 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python

from distutils.core import setup

setup(name='eta',
version='0.9',
description='ETA Progress bar for command-line utilities',
author='Marcus Breese',
author_email='[email protected]',
url='http://githum.com/mbreese/eta/',
packages=['eta'],
)

0 comments on commit 3bb011d

Please sign in to comment.