Repository: dwavesystems/dwave-cloud-client
Pull request #205 (2018-06-23 03:51:18)

CLI config create refactor: remove the readline editable support


Patch:
diff --git a/dwave/cloud/cli.py b/dwave/cloud/cli.py
index cce49dd..b38c623 100644
--- a/dwave/cloud/cli.py
+++ b/dwave/cloud/cli.py
@@ -7,7 +7,7 @@ from timeit import default_timer as timer
 
 from dwave.cloud import Client
 from dwave.cloud.utils import (
-    readline_input, click_info_switch, generate_valid_random_problem, strtrunc)
+    default_text_input, click_info_switch, generate_valid_random_problem, strtrunc)
 from dwave.cloud.package_info import __title__, __version__
 from dwave.cloud.exceptions import (
     SolverAuthenticationError, InvalidAPIResponseError, UnsupportedSolverError,
@@ -98,7 +98,8 @@ def create(config_file, profile):
         else:
             config_file = get_default_configfile_path()
             click.echo("Configuration file not found; the default location is: {}".format(config_file))
-        config_file = readline_input("Confirm configuration file path (editable): ", config_file)
+        config_file = default_text_input("Configuration file path", config_file)
+        config_file = os.path.expanduser(config_file)
 
     # create config_file path
     config_base = os.path.dirname(config_file)
@@ -127,24 +128,22 @@ def create(config_file, profile):
         else:
             profiles = 'create new'
             default_profile = 'prod'
-        while not profile:
-            profile = readline_input("Profile (%s): " % profiles, default_profile)
-            if not profile:
-                click.echo("Profile name cannot be empty.")
+        profile = default_text_input("Profile (%s)" % profiles, default_profile, optional=False)
 
     if not config.has_section(profile):
         config.add_section(profile)
 
     # fill out the profile variables
     variables = 'endpoint token client solver proxy'.split()
-    prompts = ['API endpoint URL (editable): ',
-               'Authentication token (editable): ',
-               'Client class (qpu or sw): ',
-               'Solver (can be left blank): ',
-               'Proxy URL (can be left blank): ']
+    prompts = ['API endpoint URL',
+               'Authentication token',
+               'Client class (qpu or sw)',
+               'Default solver']
     for var, prompt in zip(variables, prompts):
         default_val = config.get(profile, var, fallback=None)
-        val = readline_input(prompt, default_val)
+        val = default_text_input(prompt, default_val)
+        if val:
+            val = os.path.expandvars(val)
         if val != default_val:
             config.set(profile, var, val)
 
diff --git a/dwave/cloud/utils.py b/dwave/cloud/utils.py
index 1180fc3..4769301 100644
--- a/dwave/cloud/utils.py
+++ b/dwave/cloud/utils.py
@@ -7,7 +7,7 @@ import itertools
 import random
 
 import six
-import readline
+import click
 
 # Use numpy if available for fast decoding
 try:
@@ -17,7 +17,7 @@ except ImportError:  # pragma: no cover
     _numpy = False
 
 __all__ = ['evaluate_ising', 'uniform_iterator', 'uniform_get',
-           'readline_input', 'click_info_switch', 'datetime_to_timestamp']
+           'default_text_input', 'click_info_switch', 'datetime_to_timestamp']
 
 
 def evaluate_ising(linear, quad, state):
@@ -111,14 +111,25 @@ def strip_tail(sequence, values):
     return list(reversed(list(strip_head(reversed(sequence), values))))
 
 
-def readline_input(prompt, prefill=''):
-    """Provide an editable default for ``input()``."""
-    # see: https://stackoverflow.com/q/2533120/
-    readline.set_startup_hook(lambda: readline.insert_text(prefill))
-    try:
-        return six.moves.input(prompt)
-    finally:
-        readline.set_startup_hook()
+def default_text_input(prompt, default=None, optional=True):
+    if default:
+        prompt = "{} [{}]: ".format(prompt, default)
+    else:
+        if optional:
+            prompt = "{} [skip]: ".format(prompt)
+        else:
+            prompt = "{}: ".format(prompt)
+
+    line = ''
+    while not line:
+        line = six.moves.input(prompt)
+        if not line:
+            line = default
+        if not line:
+            if optional:
+                break
+            click.echo("Input required, please try again.")
+    return line
 
 
 def click_info_switch(f):
diff --git a/requirements.txt b/requirements.txt
index 3c4d28a..c147a36 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,5 +5,3 @@ python-dateutil>=2.7
 click>=6
 futures; python_version=="2.7"
 configparser; python_version=="2.7"
-pyreadline>=2; platform_system=="Windows"
-gnureadline>=6; os_name=="posix"
diff --git a/setup.py b/setup.py
index 21b35d6..115905e 100644
--- a/setup.py
+++ b/setup.py
@@ -23,11 +23,7 @@ extras_require = {
     'test': ['requests_mock', 'mock', 'numpy', 'coverage'],
 
     # python2 backports
-    ':python_version == "2.7"': ['futures', 'configparser'],
-
-    # readline support differs for Windows vs. POSIX (Linux/Darwin)
-    ':platform_system == "Windows"': ['pyreadline>=2'],
-    ':os_name == "posix"': ['gnureadline>=6']
+    ':python_version == "2.7"': ['futures', 'configparser']
 }
 
 # Packages provided. Only include packages under the 'dwave' namespace.


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index a677ce5..3b2f488 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -22,7 +22,7 @@ class TestCli(unittest.TestCase):
     def test_config_create(self):
         config_file = 'dwave.conf'
         profile = 'profile'
-        values = 'endpoint token client solver proxy'.split()
+        values = 'endpoint token client solver'.split()
 
         runner = CliRunner()
         with runner.isolated_filesystem():
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 9d3dee9..a24a6bf 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,8 +2,9 @@ import unittest
 from collections import OrderedDict
 
 from dwave.cloud.utils import (
-    readline_input, uniform_iterator, uniform_get, strip_head, strip_tail,
-    active_qubits, generate_valid_random_problem)
+    uniform_iterator, uniform_get, strip_head, strip_tail,
+    active_qubits, generate_valid_random_problem,
+    default_text_input)
 from dwave.cloud.testing import mock
 
 
@@ -49,12 +50,12 @@ class TestUtils(unittest.TestCase):
         self.assertEqual(active_qubits([], {(0, 1): 0}), {0, 1})
         self.assertEqual(active_qubits([0, 0], {(0, 2): 0}), {0, 1, 2})
 
-    def test_readline_input(self):
+    def test_default_text_input(self):
         val = "value"
         with mock.patch("six.moves.input", side_effect=[val], create=True):
-            self.assertEqual(readline_input("prompt", val), val)
+            self.assertEqual(default_text_input("prompt", val), val)
         with mock.patch("six.moves.input", side_effect=[val], create=True):
-            self.assertEqual(readline_input("prompt", val+val), val)
+            self.assertEqual(default_text_input("prompt", val+val), val)
 
     def test_generate_valid_random_problem(self):
         class MockSolver(object):