proglog-0.1.9/ 0000755 0000000 0000000 00000000000 13303315512 013124 5 ustar root root 0000000 0000000 proglog-0.1.9/README.rst 0000775 0000000 0000000 00000014751 13274551361 014643 0 ustar root root 0000000 0000000 .. raw:: html
Proglog is a progress logging system for Python. It allows to build complex
libraries while giving the user control on the management of logs, callbacks and progress bars.
**What problems does it solve ?** Libraries like `tqdm `_ or `progress `_ are great for quickly adding progress bars to your scripts, but become difficult to manage when building larger projects.
For instance, you will need to write different code depending on whether you are displaying the progress in a console, a Jupyter notebook, or a website.
Sometimes you need to channel the progress logs of different components into a same logger, at which case you may also let the final user choose which progress bars they want to display or to mute, even when these are handled deep down in your programs.
.. raw:: html
You may also want to log more than just progress bars, have specific callback fonctions, print the logs in human-readable format... Proglog provides all these features.
Usage
-------
Assume that you are writing a library called ``my_library`` in which you define a routine as follows:
.. code:: python
import time # for simulating computing time
from proglog import TqdmProgressBarLogger
def my_routine(iterations=10, logger='bars'):
"""Run several loops to showcase Proglog."""
if logger == 'bars':
logger = TqdmProgressBarLogger()
for i in logger.iter_bar(iteration=range(iterations)):
for j in logger.iter_bar(animal=['dog', 'cat', 'rat', 'duck']):
time.sleep(0.1) # Simulate some computing time
Now when the library users run a program in the console, they will get a console progress bar:
.. code:: python
from my_library import my_routine
my_routine()
.. raw:: html
If the users run the routine inside a Jupyter/IPython notebook, they only need to write ``proglog.notebook()`` at the beginning of the notebook to obtain HTML progress bars:
.. code:: python
import proglog
proglog.notebook()
from my_library import my_routine
my_routine()
.. raw:: html
If the user wishes to turn off all progress bars:
.. code:: python
from my_library import my_routine
from proglog import MuteProgressBarLogger
my_routine(logger=MuteProgressBarLogger())
If the user is running the routine on a web server and would want to attach the
data to an asynchronous Python-RQ job, all they need is yet a different logger:
.. code:: python
from proglog import RqWorkerBarLogger
from my_library import my_routine
logger = RqWorkerBarLogger(job=some_python_rq_job)
my_routine(logger=logger)
This allows to then display progress bars on the website such as these (see the `EGF CUBA `_ project for an example of website using Proglog):
.. raw:: html
The user may also want a custom progress logger which selectively ignores the ``animals`` progress bar, and only updates its bars every second (to save computing time):
.. code:: python
from proglog import TqdmProgressBarLogger
from my_library import my_routine
logger = TqdmProgressBarLogger(ignored_bars=('animal',),
min_time_interval=1.0)
my_routine(logger=logger)
Proglog loggers can be used for much more than just progress bars. They can in fact store any kind of data with a simple API:
.. code:: python
logger(message='Now running the main program, be patient...')
logger(current_animal='cat')
logger(last_number_tried=1235)
For more complex customization, such as adding callback functions which will be executed every time the logger's state is updated, simply create a new logger class:
.. code:: python
from proglog import ProgressBarLogger
from my_library import my_routine
class MyBarLogger(ProgressBarLogger):
def callback(self, **changes):
# Every time the logger is updated, this function is called with
# the `changes` dictionnary of the form `parameter: new value`.
for (parameter, new_value) in changes.items():
print ('Parameter %s is now %s' % (parameter, value))
logger = MyBarLogger()
my_routine(logger=logger)
When writing libraries which all log progress and may depend on each other, simply pass the Proglog logger from one program to its dependencies, to obtain one logger keeping track of all progress across libraries at once: (this implies that not two librairies use the same variables or loop names, which can be avoided by attributing prefixes to these names):
.. raw:: html
Installation
-------------
You can install Proglog through PIP
.. code:: shell
sudo pip install proglog
Alternatively, you can unzip the sources in a folder and type
.. code:: shell
sudo python setup.py install
To use the ``tqdm`` notebook-style progress bars you need to install and enable
iwidgets:
.. code:: shell
sudo pip install ipywidgets
sudo jupyter nbextension enable --py --sys-prefix widgetsnbextension
Contribute !
-------------
Proglog is an open-source software originally written at the `Edinburgh Genome Foundry
`_ by `Zulko `_
and `released on Github `_ under
the MIT licence (copyright Edinburgh Genome Foundry).
Proglog was not written by loggology experts, it *just works* with our projects and we use it a lot. Everyone is welcome to contribute if you find bugs or limitations !
proglog-0.1.9/proglog/ 0000755 0000000 0000000 00000000000 13303315512 014575 5 ustar root root 0000000 0000000 proglog-0.1.9/proglog/proglog.py 0000664 0000000 0000000 00000031702 13303223107 016623 0 ustar root root 0000000 0000000 """Implements the generic progress logger class, and the ProgressBar class.
"""
from tqdm import tqdm, tqdm_notebook
from collections import OrderedDict
import time
SETTINGS = {
'notebook': False
}
def notebook(turn='on'):
SETTINGS['notebook'] = True if (turn == 'on') else False
def troncate_string(s, max_length=25):
return s if (len(s) < max_length) else (s[:max_length] + "...")
class ProgressLogger:
"""Generic class for progress loggers.
A progress logger contains a "state" dictionnary.
Parameters
----------
init_state
Dictionnary representing the initial state.
"""
def __init__(self, init_state=None):
self.state = {}
self.stored = {}
self.logs = []
self.log_indent = 0
if init_state is not None:
self.state.update(init_state)
def log(self, message):
self.logs.append((' ' * self.log_indent) + message)
def dump_logs(self, filepath=None):
if filepath is not None:
with open(filepath, 'a') as f:
f.write("\n".join(self.logs))
else:
return "\n".join(self.logs)
def callback(self, **kw):
"""Execute something after the state has been updated by the given
state elements.
This default callback does nothing, overwrite it by subclassing
"""
pass
def store(self, **kw):
"""Store objects in the logger and trigger ``self.store_callback``.
This works exactly like ``logger()``, but the later is meant for simple
data objects (text, numbers) that will be sent over the network or
written to a file. The ``store`` method expects rather large objects
which are not necessarily serializable, and will be used eg to draw
plots on the fly.
"""
self.stored.update(kw)
self.store_callback(**kw)
def store_callback(self, **kw):
"""Execute something after the store has been updated by the given
state elements.
This default callback does nothing, overwrite it by subclassing
"""
pass
def iter(self, **kw):
"""Iterate through a list while updating the state.
Examples
--------
>>> for username in logger.iter(user=['tom', 'tim', 'lea']:
>>> # At every loop, logger.state['user'] is updated
>>> print (username)
"""
for field, iterable in kw.items():
for it in iterable:
self(**{field: it})
yield it
def __call__(self, **kw):
self.state.update(kw)
self.callback(**kw)
class ProgressBarLogger(ProgressLogger):
"""Generic class for progress loggers.
A progress logger contains a "state" dictionnary
Parameters
----------
init_state
Initial state of the logger
bars
Either None (will be initialized with no bar) or a list/tuple of bar
names (``['main', 'sub']``) which will be initialized with index -1 and
no total, or a dictionary (possibly ordered) of bars, of the form
``{bar_1: {title: 'bar1', index: 2, total:23}, bar_2: {...}}``
ignored_bars
Either None (newly met bars will be added) or a list of blacklisted bar
names, or ``'all_others'`` to signify that all bar names not already in
``self.bars`` will be ignored.
"""
bar_indent = 2
def __init__(self, init_state=None, bars=None, ignored_bars=None,
logged_bars='all', min_time_interval=0, ignore_bars_under=0):
ProgressLogger.__init__(self, init_state)
if bars is None:
bars = OrderedDict()
elif isinstance(bars, (list, tuple)):
bars = OrderedDict([
(b, dict(title=b, index=-1, total=None, message=None,
indent=0))
for b in bars
])
if isinstance(ignored_bars, (list, tuple)):
ignored_bars = set(ignored_bars)
self.ignored_bars = ignored_bars
self.logged_bars = logged_bars
self.state['bars'] = bars
self.min_time_interval = min_time_interval
self.ignore_bars_under = ignore_bars_under
@property
def bars(self):
"""Return ``self.state['bars'].``"""
return self.state['bars']
def bar_is_ignored(self, bar):
if self.ignored_bars is None:
return False
elif self.ignored_bars == 'all_others':
return (bar not in self.bars)
else:
return bar in self.ignored_bars
def bar_is_logged(self, bar):
if (not self.logged_bars):
return False
elif self.logged_bars == 'all':
return True
else:
return bar in self.logged_bars
def iterable_is_too_short(self, iterable):
length = len(iterable) if hasattr(iterable, '__len__') else None
return (length is not None) and (length < self.ignore_bars_under)
def iter_bar(self, bar_prefix='', **kw):
"""Iterate through a list while updating a state bar.
Examples
--------
>>> for username in logger.iter_bar(user=['tom', 'tim', 'lea']):
>>> # At every loop, logger.state['bars']['user'] is updated
>>> # to {index: i, total: 3, title:'user'}
>>> print (username)
"""
if 'bar_message' in kw:
bar_message = kw.pop('bar_message')
else:
bar_message = None
bar, iterable = kw.popitem()
if self.bar_is_ignored(bar) or self.iterable_is_too_short(iterable):
return iterable
bar = bar_prefix + bar
if hasattr(iterable, '__len__'):
self(**{bar + '__total': len(iterable)})
def new_iterable():
last_time = time.time()
i = 0 # necessary in case the iterator is empty
for i, it in enumerate(iterable):
now_time = time.time()
if (i == 0) or (now_time - last_time > self.min_time_interval):
if bar_message is not None:
self(**{bar + '__message': bar_message(it)})
self(**{bar + '__index': i})
last_time = now_time
yield it
if self.bars[bar]['index'] != i:
self(**{bar + '__index': i})
self(**{bar + '__index': i + 1})
return new_iterable()
def bars_callback(self, bar, attr, value, old_value=None):
"""Execute a custom action after the progress bars are updated.
Parameters
----------
bar
Name/ID of the bar to be modified.
attr
Attribute of the bar attribute to be modified
value
New value of the attribute
old_value
Previous value of this bar's attribute.
This default callback does nothing, overwrite it by subclassing.
"""
pass
def __call__(self, **kw):
items = sorted(kw.items(), key=lambda kv: not kv[0].endswith('total'))
for key, value in items:
if '__' in key:
bar, attr = key.split('__')
if self.bar_is_ignored(bar):
continue
kw.pop(key)
if bar not in self.bars:
self.bars[bar] = dict(title=bar, index=-1,
total=None, message=None)
old_value = self.bars[bar][attr]
if self.bar_is_logged(bar):
new_bar = (attr == 'index') and (value < old_value)
if (attr == 'total') or (new_bar):
self.bars[bar]['indent'] = self.log_indent
else:
self.log_indent = self.bars[bar]['indent']
self.log("[%s] %s: %s" % (bar, attr, value))
self.log_indent += self.bar_indent
self.bars[bar][attr] = value
self.bars_callback(bar, attr, value, old_value)
self.state.update(kw)
self.callback(**kw)
class TqdmProgressBarLogger(ProgressBarLogger):
"""Tqdm-powered progress bar for console or Notebooks.
Parameters
----------
init_state
Initial state of the logger
bars
Either None (will be initialized with no bar) or a list/tuple of bar
names (``['main', 'sub']``) which will be initialized with index -1 and
no total, or a dictionary (possibly ordered) of bars, of the form
``{bar_1: {title: 'bar1', index: 2, total:23}, bar_2: {...}}``
ignored_bars
Either None (newly met bars will be added) or a list of blacklisted bar
names, or ``'all_others'`` to signify that all bar names not already in
``self.bars`` will be ignored.
leave_bars
notebook
True will make the bars look nice (HTML) in the jupyter notebook. It is
advised to leave to 'default' as the default can be globally set from
inside a notebook with ``import proglog; proglog.notebook_mode()``.
print_messages
If True, every ``logger(message='something')`` will print a message in
the console / notebook
"""
def __init__(self, init_state=None, bars=None, leave_bars=False,
ignored_bars=None, logged_bars='all', notebook='default',
print_messages=True, min_time_interval=0,
ignore_bars_under=0):
ProgressBarLogger.__init__(self, init_state=init_state, bars=bars,
ignored_bars=ignored_bars,
logged_bars=logged_bars,
ignore_bars_under=ignore_bars_under,
min_time_interval=min_time_interval)
self.leave_bars = leave_bars
self.tqdm_bars = OrderedDict([
(bar, None)
for bar in self.bars
])
if notebook == 'default':
notebook = SETTINGS['notebook']
self.notebook = notebook
self.print_messages = print_messages
self.tqdm = (tqdm_notebook if self.notebook else tqdm)
def new_tqdm_bar(self, bar):
"""Create a new tqdm bar, possibly replacing an existing one."""
if (bar in self.tqdm_bars) and (self.tqdm_bars[bar] is not None):
self.close_tqdm_bar(bar)
infos = self.bars[bar]
self.tqdm_bars[bar] = self.tqdm(
total=infos['total'],
desc=infos['title'],
postfix=dict(now=troncate_string(str(infos['message']))),
leave=self.leave_bars
)
def close_tqdm_bar(self, bar):
"""Close and erase the tqdm bar"""
self.tqdm_bars[bar].close()
if not self.notebook:
self.tqdm_bars[bar] = None
def bars_callback(self, bar, attr, value, old_value):
if (bar not in self.tqdm_bars) or (self.tqdm_bars[bar] is None):
self.new_tqdm_bar(bar)
if attr == 'index':
if value >= old_value:
total = self.bars[bar]['total']
if total and (value >= total):
self.close_tqdm_bar(bar)
else:
self.tqdm_bars[bar].update(value - old_value)
else:
self.new_tqdm_bar(bar)
self.tqdm_bars[bar].update(value + 1)
elif attr == 'message':
self.tqdm_bars[bar].set_postfix(now=troncate_string(str(value)))
self.tqdm_bars[bar].update(0)
def callback(self, **kw):
if self.print_messages and ('message' in kw) and kw['message']:
if self.notebook:
print(kw['message'])
else:
self.tqdm.write(kw['message'])
class RqWorkerProgressLogger:
def __init__(self, job):
self.job = job
if 'progress_data' not in self.job.meta:
self.job.meta['progress_data'] = {}
self.job.save()
def callback(self, **kw):
self.job.meta['progress_data'] = self.state
self.job.save()
class RqWorkerBarLogger(RqWorkerProgressLogger, ProgressBarLogger):
def __init__(self, job, init_state=None, bars=None, ignored_bars=(),
logged_bars='all', min_time_interval=0):
RqWorkerProgressLogger.__init__(self, job)
ProgressBarLogger.__init__(self, init_state=init_state, bars=bars,
ignored_bars=ignored_bars,
logged_bars=logged_bars,
min_time_interval=min_time_interval)
class MuteProgressBarLogger(ProgressBarLogger):
def bar_is_ignored(self, bar):
return True
def default_bar_logger(logger, bars=None, ignored_bars=None, logged_bars='all',
min_time_interval=0, ignore_bars_under=0):
if logger == 'bar':
return TqdmProgressBarLogger(
bars=bars,
ignored_bars=ignored_bars,
logged_bars=logged_bars,
min_time_interval=min_time_interval,
ignore_bars_under=ignore_bars_under
)
elif logger is None:
return MuteProgressBarLogger()
else:
return logger
proglog-0.1.9/proglog/__init__.py 0000777 0000000 0000000 00000000456 13303222772 016727 0 ustar root root 0000000 0000000 """ geneblocks/__init__.py """
# __all__ = []
from .proglog import (ProgressLogger, ProgressBarLogger, TqdmProgressBarLogger,
notebook, RqWorkerProgressLogger, RqWorkerBarLogger,
MuteProgressBarLogger, default_bar_logger)
from .version import __version__
proglog-0.1.9/proglog/version.py 0000777 0000000 0000000 00000000026 13303315201 016634 0 ustar root root 0000000 0000000 __version__ = "0.1.9"
proglog-0.1.9/ez_setup.py 0000777 0000000 0000000 00000020500 13123300122 015327 0 ustar root root 0000000 0000000
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
import shutil
import sys
import tempfile
import tarfile
import optparse
import subprocess
from distutils import log
try:
from site import USER_SITE
except ImportError:
USER_SITE = None
DEFAULT_VERSION = "0.9.6"
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
def _python_cmd(*args):
args = (sys.executable,) + args
return subprocess.call(args) == 0
def _install(tarball, install_args=()):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# installing
log.warn('Installing Setuptools')
if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
# exitcode will be 2
return 2
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
def _build_egg(egg, tarball, to_dir):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# building an egg
log.warn('Building a Setuptools egg in %s', to_dir)
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
raise IOError('Could not build the egg.')
def _do_download(version, download_base, to_dir, download_delay):
egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
% (version, sys.version_info[0], sys.version_info[1]))
if not os.path.exists(egg):
tarball = download_setuptools(version, download_base,
to_dir, download_delay)
_build_egg(egg, tarball, to_dir)
sys.path.insert(0, egg)
import setuptools
setuptools.bootstrap_install_from = egg
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, download_delay=15):
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
was_imported = 'pkg_resources' in sys.modules or \
'setuptools' in sys.modules
try:
import pkg_resources
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
pkg_resources.require("setuptools>=" + version)
return
except pkg_resources.VersionConflict:
e = sys.exc_info()[1]
if was_imported:
sys.stderr.write(
"The required version of setuptools (>=%s) is not available,\n"
"and can't be installed while this script is running. Please\n"
"install a more recent version first, using\n"
"'easy_install -U setuptools'."
"\n\n(Currently using %r)\n" % (version, e.args[0]))
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return _do_download(version, download_base, to_dir,
download_delay)
except pkg_resources.DistributionNotFound:
return _do_download(version, download_base, to_dir,
download_delay)
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download
attempt.
"""
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
tgz_name = "setuptools-%s.tar.gz" % version
url = download_base + tgz_name
saveto = os.path.join(to_dir, tgz_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
log.warn("Downloading %s", url)
src = urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = src.read()
dst = open(saveto, "wb")
dst.write(data)
finally:
if src:
src.close()
if dst:
dst.close()
return os.path.realpath(saveto)
def _extractall(self, path=".", members=None):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
directories afterwards. `path' specifies a different directory
to extract to. `members' is optional and must be a subset of the
list returned by getmembers().
"""
import copy
import operator
from tarfile import ExtractError
directories = []
if members is None:
members = self
for tarinfo in members:
if tarinfo.isdir():
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
if sys.version_info < (2, 4):
def sorter(dir1, dir2):
return cmp(dir1.name, dir2.name)
directories.sort(sorter)
directories.reverse()
else:
directories.sort(key=operator.attrgetter('name'), reverse=True)
# Set correct owner, mtime and filemode on directories.
for tarinfo in directories:
dirpath = os.path.join(path, tarinfo.name)
try:
self.chown(tarinfo, dirpath)
self.utime(tarinfo, dirpath)
self.chmod(tarinfo, dirpath)
except ExtractError:
e = sys.exc_info()[1]
if self.errorlevel > 1:
raise
else:
self._dbg(1, "tarfile: %s" % e)
def _build_install_args(options):
"""
Build the arguments to 'python setup.py install' on the setuptools package
"""
install_args = []
if options.user_install:
if sys.version_info < (2, 6):
log.warn("--user requires Python 2.6 or later")
raise SystemExit(1)
install_args.append('--user')
return install_args
def _parse_args():
"""
Parse the command line for options
"""
parser = optparse.OptionParser()
parser.add_option(
'--user', dest='user_install', action='store_true', default=False,
help='install in user site package (requires Python 2.6 or later)')
parser.add_option(
'--download-base', dest='download_base', metavar="URL",
default=DEFAULT_URL,
help='alternative URL from where to download the setuptools package')
options, args = parser.parse_args()
# positional arguments are ignored
return options
def main(version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
options = _parse_args()
tarball = download_setuptools(download_base=options.download_base)
return _install(tarball, _build_install_args(options))
if __name__ == '__main__':
sys.exit(main())
proglog-0.1.9/setup.cfg 0000644 0000000 0000000 00000000046 13303315512 014745 0 ustar root root 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
proglog-0.1.9/proglog.egg-info/ 0000755 0000000 0000000 00000000000 13303315512 016267 5 ustar root root 0000000 0000000 proglog-0.1.9/proglog.egg-info/SOURCES.txt 0000664 0000000 0000000 00000000433 13303315512 020155 0 ustar root root 0000000 0000000 LICENCE.txt
MANIFEST.in
README.rst
ez_setup.py
pypi-readme.rst
setup.py
proglog/__init__.py
proglog/proglog.py
proglog/version.py
proglog.egg-info/PKG-INFO
proglog.egg-info/SOURCES.txt
proglog.egg-info/dependency_links.txt
proglog.egg-info/requires.txt
proglog.egg-info/top_level.txt proglog-0.1.9/proglog.egg-info/top_level.txt 0000664 0000000 0000000 00000000010 13303315512 021012 0 ustar root root 0000000 0000000 proglog
proglog-0.1.9/proglog.egg-info/PKG-INFO 0000664 0000000 0000000 00000001530 13303315512 017365 0 ustar root root 0000000 0000000 Metadata-Version: 1.0
Name: proglog
Version: 0.1.9
Summary: Log and progress bar manager for console, notebooks, web...
Home-page: UNKNOWN
Author: Zulko
Author-email: UNKNOWN
License: MIT - copyright Edinburgh Genome Foundry
Description: Proglog
===================
Proglog is a progress logging system for Python. It allows to build complex
libraries while giving the user control on the management of logs, callbacks and progress bars.
Infos
-----
**PIP installation:**
.. code:: bash
pip install proglog
**Github Page**
``_
**License:** MIT, Copyright Edinburgh Genome Foundry
Keywords: logger log progress bar
Platform: UNKNOWN
proglog-0.1.9/proglog.egg-info/dependency_links.txt 0000664 0000000 0000000 00000000001 13303315512 022337 0 ustar root root 0000000 0000000
proglog-0.1.9/proglog.egg-info/requires.txt 0000664 0000000 0000000 00000000005 13303315512 020664 0 ustar root root 0000000 0000000 tqdm
proglog-0.1.9/PKG-INFO 0000644 0000000 0000000 00000001530 13303315512 014220 0 ustar root root 0000000 0000000 Metadata-Version: 1.0
Name: proglog
Version: 0.1.9
Summary: Log and progress bar manager for console, notebooks, web...
Home-page: UNKNOWN
Author: Zulko
Author-email: UNKNOWN
License: MIT - copyright Edinburgh Genome Foundry
Description: Proglog
===================
Proglog is a progress logging system for Python. It allows to build complex
libraries while giving the user control on the management of logs, callbacks and progress bars.
Infos
-----
**PIP installation:**
.. code:: bash
pip install proglog
**Github Page**
``_
**License:** MIT, Copyright Edinburgh Genome Foundry
Keywords: logger log progress bar
Platform: UNKNOWN
proglog-0.1.9/pypi-readme.rst 0000664 0000000 0000000 00000000625 13303315445 016104 0 ustar root root 0000000 0000000 Proglog
===================
Proglog is a progress logging system for Python. It allows to build complex
libraries while giving the user control on the management of logs, callbacks and progress bars.
Infos
-----
**PIP installation:**
.. code:: bash
pip install proglog
**Github Page**
``_
**License:** MIT, Copyright Edinburgh Genome Foundry
proglog-0.1.9/setup.py 0000777 0000000 0000000 00000001014 13303315427 014646 0 ustar root root 0000000 0000000 import ez_setup
ez_setup.use_setuptools()
from setuptools import setup, find_packages
exec(open('proglog/version.py').read()) # loads __version__
setup(name='proglog',
version=__version__,
author='Zulko',
description='Log and progress bar manager for console, notebooks, web...',
long_description=open('pypi-readme.rst').read(),
license='MIT - copyright Edinburgh Genome Foundry',
keywords="logger log progress bar",
install_requires=['tqdm'],
packages= find_packages(exclude='docs'))
proglog-0.1.9/LICENCE.txt 0000777 0000000 0000000 00000002162 13143677502 014753 0 ustar root root 0000000 0000000
The MIT License (MIT)
[OSI Approved License]
The MIT License (MIT)
Copyright (c) 2017 Edinburgh Genome Foundry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
proglog-0.1.9/MANIFEST.in 0000777 0000000 0000000 00000000060 13144124614 014671 0 ustar root root 0000000 0000000 include *.txt
include *.rst
include ez_setup.py