Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Fixes
Enhancements

Changes
* Removes deprecated ProgressMeter (Issue #2739)
* Removes deprecated MDAnalysis.units.N_Avogadro (PR #2737)
* Dropped Python 2 support

Expand Down
3 changes: 1 addition & 2 deletions package/MDAnalysis/analysis/density.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@
from MDAnalysis import NoDataError, MissingDataWarning
from .. import units
from ..lib import distances
from MDAnalysis.lib.log import ProgressBar
from MDAnalysis.lib.log import ProgressMeter # remove in 2.0
from MDAnalysis.lib.log import ProgressBar
from MDAnalysis.lib.util import deprecate

from .base import AnalysisBase
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/analysis/hbonds/hbond_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -897,8 +897,8 @@ def run(self, start=None, stop=None, step=None, verbose=None, **kwargs):
read every `step` between `start` (included) and `stop` (excluded),
``None`` selects 1. [``None``]
verbose : bool (optional)
toggle progress meter output :class:`~MDAnalysis.lib.log.ProgressMeter`
[``True``]
toggle progress meter output
:class:`~MDAnalysis.lib.log.ProgressBar` [``True``]
debug : bool (optional)
enable detailed logging of debugging information; this can create
*very big* log files so it is disabled (``False``) by default; setting
Expand Down
194 changes: 5 additions & 189 deletions package/MDAnalysis/lib/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,18 @@
Other functions and classes for logging purposes
------------------------------------------------


.. versionchanged:: 2.0.0
Deprecated :class:`MDAnalysis.lib.log.ProgressMeter` has now been removed.

.. autogenerated, see Online Docs

"""
from __future__ import print_function, division, absolute_import
from __future__ import absolute_import

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll remove all the py2 stuff from lib at the same time.

import sys
import logging
import re
import warnings

from tqdm.auto import tqdm

Expand Down Expand Up @@ -224,193 +227,6 @@ def _guess_string_format(template):
return _legacy_format


class ProgressMeter(object):
r"""Simple progress meter (Deprecated)

..Warning:
This class is deprecated and will be removed in version 2.0.
Please use :class:`MDAnalysis.lib.log.ProgressBar` instead.

The :class:`ProgressMeter` class can be used to show running progress such
as frames processed or percentage done to give the user feedback on the
duration and progress of a task. It is structures as a class that is
customized on instantation (e.g., using a custom `format` string and the
expected total number of frames to be processed). Within a processing loop,
call :meth:`echo` with the current frame number to print the output to
stderr.

Parameters
----------
numsteps: int
total number of steps
interval: int
only calculate and print progress every `interval` steps [10]
format: str
a format string with Python variable interpolation. Allowed
values:

* *step*: current step
* *numsteps*: total number of steps as supplied in *numsteps*
* *percentage*: percentage of total

The last call to :meth:`ProgressMeter.print` will automatically
issue a newline ``\n``.

If *format* is ``None`` then the default is used::

Step {step:5d}/{numsteps} [{percentage:5.1f}%]

offset: int
number to add to *step*; e.g. if *step* is 0-based (as in MDAnalysis)
then one should set *offset* = 1; for 1-based steps, choose 0. [1]
verbose: bool
If ``False``, disable all output, ``True`` print all messages as
specified, [``True``]
dynamic: bool
If ``True``, each line will be printed on top of the previous one.
This is done by prepedind the format with ``\r``. [``True``]
format_handling: str
how to handle the format string. Allowed values are:

* *new*: the format string uses {}-based formating
* *legacy*: the format string uses %-basedd formating
* *auto*: default, try to guess how the format string should be
handled


Examples
--------
The typical use case is to show progress as frames of a trajectory are
processed::

u = Universe(PSF, DCD)
pm = ProgressMeter(u.trajectory.n_frames, interval=100)
for ts in u.trajectory:
pm.echo(ts.frame)
...

For a trajectory with 10000 frames this will produce output such
as ::

Step 100/10000 [ 1.0%]
Step 200/10000 [ 2.0%]
...

The default *format* string is::

Step {step:5d}/{numsteps} [{percentage:5.1f}%]

By default, each line of the progress meter is displayed on top of the
previous one. To prevent this behaviour, set the `dynamic` keyword to
``False``.

It is possible to embed (almost) arbitrary additional data in the
format string, for example a current RMSD value::

format_line = "RMSD {rmsd:5.2f} at {step:5d}/{numsteps} [{percentage:5.1f}%]"
pm = ProgressMeter(u.trajectory.n_frames,
interval=100, format=format_line)
for ts in u.trajectory:
pm.echo(ts.frame, rmsd=current_rmsd)
...

This will print something like::

RMSD 1.02 at 100/10000 [ 1.0%]
RMSD 1.89 at 200/10000 [ 2.0%]
...

.. versionchanged:: 0.8
Keyword argument *quiet* was added.

.. versionchanged:: 0.16
Keyword argument *dynamic* replaces ``\r`` in the format.

.. deprecated:: 0.16
Keyword argument *quiet* is deprecated in favor of *verbose*.

.. deprecated:: 1.0
This class is deprecated in favor of *ProgressBar*.

"""

def __init__(self, numsteps, format=None, interval=10, offset=1,
verbose=True, dynamic=True,
format_handling='auto'):
warnings.warn(
"This class is deprecated as of MDAnalysis version 1.0. "
"It will be removed in MDAnalysis version 2.0. "
"Please use MDAnalysis.lib.log.ProgressBar instead.",
category=DeprecationWarning
)
self.numsteps = numsteps
self.interval = int(interval)
self.offset = int(offset)
self.verbose = verbose
self.dynamic = dynamic
self.numouts = -1

if format is None:
format = "Step {step:5d}/{numsteps} [{percentage:5.1f}%]"
self.format_handler = _new_format
else:
if format_handling == 'auto':
self.format_handler = _guess_string_format(format)
else:
self.format_handler = {'new': _new_format,
'legacy': _legacy_format}[format_handling]
self.format = format
self.step = 0
self.percentage = 0.0
assert numsteps > 0, "numsteps step must be >0"
assert interval > 0, "interval step must be >0"

def update(self, step, **kwargs):
"""Update the state of the ProgressMeter.

*kwargs* are additional attributes that can be references in
the format string.
"""
self.step = step + self.offset
self.percentage = 100. * self.step / self.numsteps
for k, v in kwargs.items():
setattr(self, k, v)

self.numouts += 1

def echo(self, step, **kwargs):
"""Print the state to stderr, but only every *interval* steps.

1) calls :meth:`~ProgressMeter.update`
2) writes step and percentage to stderr with :func:`echo`,
using the format string (in :attr:`ProgressMeter.format`)

The last step is always shown, even if not on an *interval*, and a
carriage return is replaced with a new line for a cleaner display.

*kwargs* are additional attributes that can be references in
the format string.

.. Note:: If *verbose* = ``False`` has been set in the
constructor or if :attr:`ProgressMeter.verbose` has
been set to ``False``, then no messages will be
printed.
"""
if not self.verbose:
return
self.update(step, **kwargs)
format = self.format
newline = not self.dynamic
if self.step == self.numsteps:
newline = True
elif self.numouts % self.interval == 0:
pass
else:
return
echo(self.format_handler(format, vars(self)),
replace=self.dynamic, newline=newline)


class ProgressBar(tqdm):
def __init__(self, *args, **kwargs):
verbose = kwargs.pop('verbose', True)
Expand Down
25 changes: 1 addition & 24 deletions testsuite/MDAnalysisTests/lib/test_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,9 @@
import warnings
import pytest

from MDAnalysis.lib.log import ProgressMeter, ProgressBar
from MDAnalysis.lib.log import ProgressBar


class TestProgressMeter(object):

def test_deprecated(self, capsys):
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Trigger a warning.
pm = ProgressMeter(10)
# Verify the warning
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "MDAnalysis.lib.log.ProgressBar" in str(w[-1].message)

def test_output(self, capsys):
pm = ProgressMeter(10, interval=1)
for i in range(10):
pm.echo(i)
out, err = capsys.readouterr()
expected = 'Step 10/10 [100.0%]'
actual = err.strip().split('\r')[-1]
assert actual == expected


class TestProgressBar(object):

def test_output(self, capsys):
Expand Down
71 changes: 0 additions & 71 deletions testsuite/MDAnalysisTests/utils/test_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,74 +72,3 @@ def buffer():
def _assert_in(output, string):
assert string in output, "Output '{0}' does not match required format '{1}'.".format(output.replace('\r', '\\r'), string.replace('\r', '\\r'))


def test_default_ProgressMeter(buffer, n=101, interval=10):
template = "Step {step:5d}/{numsteps} [{percentage:5.1f}%]"
with RedirectedStderr(buffer):
pm = MDAnalysis.lib.log.ProgressMeter(n, interval=interval)
for frame in range(n):
pm.echo(frame)
buffer.seek(0)
output = "".join(buffer.readlines())
_assert_in(output, ('\r' + template).format(**{'step': 1, 'numsteps': n, 'percentage': 100./n}))
# last line always ends with \n!
_assert_in(output,
('\r' + template + '\n').format(**{'step': n, 'numsteps': n,
'percentage': 100.}))


def test_custom_ProgressMeter(buffer, n=51, interval=7):
template = "RMSD {rmsd:5.2f} at {step:03d}/{numsteps:4d} [{percentage:5.1f}%]"
with RedirectedStderr(buffer):
pm = MDAnalysis.lib.log.ProgressMeter(n, interval=interval,
format=template, offset=1)
for frame in range(n):
# n+1/n correction for 0-based frame vs 1-based counting
rmsd = 0.02 * frame * (n+1)/ n
pm.echo(frame, rmsd=rmsd)
buffer.seek(0)
output = "".join(buffer.readlines())
_assert_in(output,
('\r' + template).format(**{'rmsd': 0.0, 'step': 1,
'numsteps': n, 'percentage': 100./n}))
# last line always ends with \n!
_assert_in(output,
('\r' + template + '\n').format(
**{'rmsd': 0.02*n, 'step': n,
'numsteps': n, 'percentage': 100.0}))


def test_legacy_ProgressMeter(buffer, n=51, interval=7):
template = "RMSD %(rmsd)5.2f at %(step)03d/%(numsteps)4d [%(percentage)5.1f%%]"
with RedirectedStderr(buffer):
pm = MDAnalysis.lib.log.ProgressMeter(n, interval=interval,
format=template, offset=1)
for frame in range(n):
# n+1/n correction for 0-based frame vs 1-based counting
rmsd = 0.02 * frame * (n+1)/ n
pm.echo(frame, rmsd=rmsd)
buffer.seek(0)
output = "".join(buffer.readlines())
_assert_in(output,
('\r' + template) % {'rmsd': 0.0, 'step': 1,
'numsteps': n, 'percentage': 100./n})
# last line always ends with \n!
_assert_in(output,
('\r' + template + '\n') % {'rmsd': 0.02*n, 'step': n,
'numsteps': n, 'percentage': 100.0})


@pytest.mark.parametrize('step, percentage', [
(1, 100./51),
(51, 100.)
])
def test_not_dynamic_ProgressMeter(buffer, step, percentage, n=51, interval=10):
template = "Step {step:5d}/{numsteps} [{percentage:5.1f}%]"
with RedirectedStderr(buffer):
pm = MDAnalysis.lib.log.ProgressMeter(n, interval=interval,
dynamic=False)
for frame in range(n):
pm.echo(frame)
buffer.seek(0)
output = "".join(buffer.readlines())
_assert_in(output, (template + '\n').format(**{'step': step, 'numsteps': n, 'percentage': percentage}))