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
9 changes: 9 additions & 0 deletions paths_cli/tests/wizard/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@

from paths_cli.compat.openmm import HAS_OPENMM, mm, unit

from paths_cli.wizard import pause


@pytest.fixture(autouse=True, scope='session')
def pause_style_testing():
pause.set_pause_style('testing')
yield


# TODO: this isn't wizard-specific, and should be moved somwhere more
# generally useful (like, oh, maybe openpathsampling.tests.fixtures?)
@pytest.fixture
Expand Down
4 changes: 4 additions & 0 deletions paths_cli/tests/wizard/mock_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def input(self, content):
self.log.append(content + " " + user_input)
return user_input

def draw_hline(self):
# we don't even bother for the mock console
pass

@property
def log_text(self):
return "\n".join(self.log)
Expand Down
72 changes: 72 additions & 0 deletions paths_cli/tests/wizard/test_pause.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import pytest
import time
from paths_cli.tests.wizard.mock_wizard import mock_wizard

from paths_cli.wizard.pause import *

@pytest.fixture(autouse=True, scope="module")
def use_default_pause_style():
with pause_style('default'):
yield


def test_get_pause_style():
default_style = PAUSE_STYLES['default']
assert get_pause_style() == default_style


@pytest.mark.parametrize('input_type', ['string', 'tuple', 'PauseStyle'])
def test_set_pause_style(input_type):
# check that we have the default settings
default_style = PAUSE_STYLES['default']
test_style = PAUSE_STYLES['testing']
input_val = {
'string': 'testing',
'tuple': tuple(test_style),
'PauseStyle': PauseStyle(*test_style)
}[input_type]
assert input_val is not test_style # always a different object
assert get_pause_style() == default_style
set_pause_style(input_val)
assert get_pause_style() != default_style
assert get_pause_style() == test_style
set_pause_style('default') # explicitly reset default


def test_set_pause_bad_name():
with pytest.raises(RuntimeError, match="Unknown pause style"):
set_pause_style('foo')

# ensure we didn't break anything for later tests
assert get_pause_style() == PAUSE_STYLES['default']


def test_pause_style_context():
assert get_pause_style() == PAUSE_STYLES['default']
with pause_style('testing'):
assert get_pause_style() == PAUSE_STYLES['testing']
assert get_pause_style() == PAUSE_STYLES['default']


def _run_pause_test(func):
test_style = PAUSE_STYLES['testing']
expected = getattr(test_style, func.__name__)
wiz = mock_wizard([])
with pause_style(test_style):
start = time.time()
func(wiz)
duration = time.time() - start
assert expected <= duration < 1.1 * duration
return wiz


def test_short():
_ = _run_pause_test(short)


def test_long():
_ = _run_pause_test(long)


def test_section():
wiz = _run_pause_test(section)
2 changes: 1 addition & 1 deletion paths_cli/tests/wizard/test_volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
volume_ask
)


import openpathsampling as paths
from openpathsampling.experimental.storage.collective_variables import \
CoordinateFunctionCV

from openpathsampling.tests.test_helpers import make_1d_traj


def _wrap(x, period_min, period_max):
# used in testing periodic CVs
while x >= period_max:
Expand Down
71 changes: 71 additions & 0 deletions paths_cli/wizard/pause.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import time
import contextlib
from collections import namedtuple

PauseStyle = namedtuple("PauseStyle", ['short', 'long', 'section'])

PAUSE_STYLES = {
'testing': PauseStyle(0.01, 0.03, 0.05),
'default': PauseStyle(0.1, 0.5, 0.75),
'nopause': PauseStyle(0.0, 0.0, 0.0),
}

_PAUSE_STYLE = PAUSE_STYLES['default']


@contextlib.contextmanager
def pause_style(style):
"""Context manager for pause styles.

Parameters
----------
style : :class:`.PauseStyle`
pause style to use within the context
"""
old_style = get_pause_style()
try:
set_pause_style(style)
yield
finally:
set_pause_style(old_style)


def get_pause_style():
"""Get the current pause style"""
return _PAUSE_STYLE


def set_pause_style(style):
"""Set the pause style

Parameters
----------
pause_style : :class:`.PauseStyle` or str
pause style to use, can be a string if the style is registered in
pause.PAUSE_STYLES
"""
global _PAUSE_STYLE
if isinstance(style, str):
try:
_PAUSE_STYLE = PAUSE_STYLES[style]
except KeyError as exc:
raise RuntimeError(f"Unknown pause style: '{style}'") from exc
else:
_PAUSE_STYLE = style


def section(wizard):
"""Section break (pause and possible visual cue).
"""
time.sleep(_PAUSE_STYLE.section)
wizard.console.draw_hline()


def long(wizard):
"""Long pause from the wizard"""
time.sleep(_PAUSE_STYLE.long)


def short(wizard):
"""Short pause from the wizard"""
time.sleep(_PAUSE_STYLE.short)
2 changes: 2 additions & 0 deletions paths_cli/wizard/two_state_tps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
SINGLE_ENGINE_STEP, CVS_STEP, WizardStep
)
from paths_cli.wizard.wizard import Wizard
from paths_cli.wizard import pause

volumes = get_category_wizard('volume')
from paths_cli.wizard.volumes import _FIRST_STATE, _VOL_DESC
Expand All @@ -18,6 +19,7 @@ def two_state_tps(wizard, fixed_length=False):
]
initial_state = volumes(wizard, context={'intro': intro})
wizard.register(initial_state, 'initial state', 'states')
pause.section(wizard)
intro = [
"Next let's define your final state.",
_VOL_DESC
Expand Down
9 changes: 7 additions & 2 deletions paths_cli/wizard/volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@
from paths_cli.wizard.helper import EvalHelperFunc, Helper
from paths_cli.wizard.core import interpret_req
import paths_cli.compiling.volumes
from paths_cli.wizard import pause


def _binary_func_volume(wizard, context, op):
wizard.say("Let's make the first constituent volume:")
wizard.say("Let's make the first constituent volume.")
pause.long(wizard)
new_context = volume_set_context(wizard, context, selected=None)
new_context['part'] = 1
vol1 = VOLUMES_PLUGIN(wizard, new_context)
wizard.say("Let's make the second constituent volume:")
pause.section(wizard)
wizard.say("Let's make the second constituent volume.")
pause.long(wizard)
new_context['part'] = 2
vol2 = VOLUMES_PLUGIN(wizard, new_context)
pause.section(wizard)
wizard.say("Now we'll combine those two constituent volumes...")
vol = op(vol1, vol2)
return vol
Expand Down
6 changes: 6 additions & 0 deletions paths_cli/wizard/wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from paths_cli.wizard.helper import Helper, QuitWizard
from paths_cli.compiling.tools import custom_eval

from paths_cli.wizard import pause

import shutil

class Console: # no-cov
Expand All @@ -26,6 +28,9 @@ def input(self, content):
def width(self):
return shutil.get_terminal_size((80, 24)).columns

def draw_hline(self):
self.print('═' * self.width)

class Wizard:
def __init__(self, steps):
self.steps = steps
Expand Down Expand Up @@ -299,6 +304,7 @@ def _do_one(self, step, req):
self.say("Okay, let's try that again.")
return True
self.register(obj, step.display_name, step.store_name)
pause.section(self)
requires_another, allows_another = self._req_do_another(req)
if requires_another:
do_another = True
Expand Down