Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add interactive CLI to save user account #2066

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

frankharkins
Copy link
Member

@frankharkins frankharkins commented Dec 2, 2024

Summary

Closes #2054.

This PR adds an interactive command-line interface (CLI) that guides users through saving their account to disk. This should be easier for users as they can just run one command and be guided through the relevant choices, and it should be more secure as they're discouraged from storing their token in code they might share.

With qiskit-ibm-runtime installed, users will be able to run the following command to start the CLI.

qiskit-ibm-runtime save-account

This should also be compatible with pipx run, so users will be able to run pipx run qiskit-ibm-runtime save-account from any environment.

Examples

Here's a screenshot of me saving my account correctly.

Screenshot 2024-12-02 at 20 35 29

Here's the output of --help:

(.venv) ~ $ qiskit-ibm-runtime --help      
usage: qiskit-ibm-runtime [-h] {save-account} ...

Scripts for the Qiskit IBM Runtime Python package

options:
  -h, --help      show this help message and exit

Scripts:
  This package supports the following scripts:

  {save-account}
    save-account  Interactive command-line interface to save your account locally.

Questions

What should I add to the documentation? I don't think it belongs in the API documentation in this repo as we don't want users to import these functions. Maybe we can add a note under QiskitRuntimeService.save_account recommending this instead? When released, we can update the guides.

@frankharkins frankharkins marked this pull request as ready for review December 2, 2024 21:18
Copy link
Collaborator

@Eric-Arellano Eric-Arellano left a comment

Choose a reason for hiding this comment

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

This is lovely!

"""
while True:
response = input(message + " ").strip()
if response == "quit":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe q? You could accept q or quit if you want both. Developers are lazy

)

@classmethod
def print_box(cls, lines: List[str]) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe move to Format?

return choice


class Format:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice! It's not always safe to use color codes, such as when piping output or in certain terminals. I recommend renaming this to be Formatter and setting an attribute supports_ansi_codes. Then, change the methods to be instance methods that use supports_ansi_codes.

Determining the color code support is tricky. https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python links to Django's approach in the first answer (click the link for the latest). Imo it's not critical that we always correctly detect things; it can be best effort.

It's also common for CLIs to have an option --colors and --no-colors to force the CLI to behave a certain way.

# that they have been altered from the originals.

"""
The `save-account` command-line interface. These classes and functions are not public.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit that Python docstring is meant to have one sentence as the summary, then a new paragraph, then any supplemental text. https://peps.python.org/pep-0257/

Suggested change
The `save-account` command-line interface. These classes and functions are not public.
The `save-account` command-line interface.
These classes and functions are not public.

This applies to several docstrings in this file.

The `save-account` command-line interface. These classes and functions are not public.
"""

import argparse
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good choice to use argparse rather than third-party libraries like click. Keep the dependency list smaller.

# Use argparse to create the --help feature
parser = argparse.ArgumentParser(
prog="qiskit-ibm-runtime",
description="Scripts for the Qiskit IBM Runtime Python package",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe call this commands? That is more conventional with a CLI. Ditto the other references to "scripts"

]
print("\n".join(box_lines))

@classmethod
Copy link
Collaborator

Choose a reason for hiding this comment

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

These and the following methods should be @staticmethod since they don't use cls. That makes the interface more clear that you more quickly realize it's only a utility method. https://www.geeksforgeeks.org/class-method-vs-static-method-python/

Comment on lines +130 to +133
while True:
token = getpass("Token: ").strip()
if token != "":
return token
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe worth making a helper function

)


def user_input(message: str, is_valid: Callable[[str], bool]) -> str:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe worth grouping these user input helper functions as static methods in a UserInput class. I like the grouping you're doing because it makes this single-file program better organized.

class MockIO:
"""Mock `input` and `getpass`"""

# pylint: disable=missing-function-docstring
Copy link
Collaborator

Choose a reason for hiding this comment

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

Imo this can be set at the file level since it's a test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider CLI for saving account
2 participants