launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #13113
[Merge] lp:~jtv/maas/maascli-split-cli-bis into lp:maas
Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/maascli-split-cli-bis into lp:maas.
Commit message:
Split maascli's CLI management commands off into their own module, away from the registration of API commands.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jtv/maas/maascli-split-cli-bis/+merge/128425
This was getting in the way of my attempts at a structural rearrangement to make maascli more user-friendly. Next I'll introduce a new "cli" sub-command to encapsulate the CLI management commands, and make profile selection optional so that the API commands are directly in the maascli tool's main namespace.
Jeroen
--
https://code.launchpad.net/~jtv/maas/maascli-split-cli-bis/+merge/128425
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/maas/maascli-split-cli-bis into lp:maas.
=== modified file 'src/maascli/__init__.py'
--- src/maascli/__init__.py 2012-10-05 05:12:01 +0000
+++ src/maascli/__init__.py 2012-10-08 06:34:23 +0000
@@ -19,10 +19,8 @@
import sys
from bzrlib import osutils
-from maascli.api import (
- register_api_commands,
- register_cli_commands,
- )
+from maascli.api import register_api_commands
+from maascli.cli import register_cli_commands
from maascli.utils import parse_docstring
=== modified file 'src/maascli/api.py'
--- src/maascli/api.py 2012-10-08 05:24:57 +0000
+++ src/maascli/api.py 2012-10-08 06:34:23 +0000
@@ -12,7 +12,6 @@
__metaclass__ = type
__all__ = [
"register_api_commands",
- "register_cli_commands",
]
from email.message import Message
@@ -25,19 +24,16 @@
urlparse,
)
-from apiclient.creds import convert_tuple_to_string
from apiclient.maas_client import MAASOAuth
from apiclient.multipart import encode_multipart_data
from apiclient.utils import ascii_url
import httplib2
-from maascli.auth import obtain_credentials
from maascli.command import (
Command,
CommandError,
)
from maascli.config import ProfileConfig
from maascli.utils import (
- ensure_trailing_slash,
handler_command_name,
parse_docstring,
safe_name,
@@ -105,97 +101,6 @@
print(form % (cap(header), headers[header]), file=file)
-class cmd_login(Command):
- """Log-in to a remote API, storing its description and credentials.
-
- If credentials are not provided on the command-line, they will be prompted
- for interactively.
- """
-
- def __init__(self, parser):
- super(cmd_login, self).__init__(parser)
- parser.add_argument(
- "profile_name", metavar="profile-name", help=(
- "The name with which you will later refer to this remote "
- "server and credentials within this tool."
- ))
- parser.add_argument(
- "url", help=(
- "The URL of the remote API, e.g. "
- "http://example.com/MAAS/api/1.0/"))
- parser.add_argument(
- "credentials", nargs="?", default=None, help=(
- "The credentials, also known as the API key, for the "
- "remote MAAS server. These can be found in the user "
- "preferences page in the web UI; they take the form of "
- "a long random-looking string composed of three parts, "
- "separated by colons."
- ))
- parser.set_defaults(credentials=None)
-
- def __call__(self, options):
- # Try and obtain credentials interactively if they're not given, or
- # read them from stdin if they're specified as "-".
- credentials = obtain_credentials(options.credentials)
- # Normalise the remote service's URL.
- url = ensure_trailing_slash(options.url)
- # Get description of remote API.
- description = fetch_api_description(url)
- # Save the config.
- profile_name = options.profile_name
- with ProfileConfig.open() as config:
- config[profile_name] = {
- "credentials": credentials,
- "description": description,
- "name": profile_name,
- "url": url,
- }
-
-
-class cmd_refresh(Command):
- """Refresh the API descriptions of all profiles."""
-
- def __call__(self, options):
- with ProfileConfig.open() as config:
- for profile_name in config:
- profile = config[profile_name]
- url = profile["url"]
- profile["description"] = fetch_api_description(url)
- config[profile_name] = profile
-
-
-class cmd_logout(Command):
- """Log-out of a remote API, purging any stored credentials."""
-
- def __init__(self, parser):
- super(cmd_logout, self).__init__(parser)
- parser.add_argument(
- "profile_name", metavar="profile-name", help=(
- "The name with which a remote server and its credentials "
- "are referred to within this tool."
- ))
-
- def __call__(self, options):
- with ProfileConfig.open() as config:
- del config[options.profile_name]
-
-
-class cmd_list(Command):
- """List remote APIs that have been logged-in to."""
-
- def __call__(self, options):
- with ProfileConfig.open() as config:
- for profile_name in config:
- profile = config[profile_name]
- url = profile["url"]
- creds = profile["credentials"]
- if creds is None:
- print(profile_name, url)
- else:
- creds = convert_tuple_to_string(creds)
- print(profile_name, url, creds)
-
-
class Action(Command):
"""A generic MAAS API action.
@@ -348,23 +253,6 @@
register_actions(profile, handler, handler_parser)
-commands = {
- 'login': cmd_login,
- 'logout': cmd_logout,
- 'list': cmd_list,
- 'refresh': cmd_refresh,
-}
-
-
-def register_cli_commands(parser):
- """Register the CLI's meta-subcommands on `parser`."""
- for name, command in commands.items():
- help_title, help_body = parse_docstring(command)
- command_parser = parser.subparsers.add_parser(
- safe_name(name), help=help_title, description=help_body)
- command_parser.set_defaults(execute=command(command_parser))
-
-
def register_api_commands(parser):
"""Register all profiles as subcommands on `parser`."""
with ProfileConfig.open() as config:
=== added file 'src/maascli/cli.py'
--- src/maascli/cli.py 1970-01-01 00:00:00 +0000
+++ src/maascli/cli.py 2012-10-08 06:34:23 +0000
@@ -0,0 +1,134 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""CLI management commands."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = [
+ 'register_cli_commands',
+ ]
+
+from apiclient.creds import convert_tuple_to_string
+from maascli.api import fetch_api_description
+from maascli.auth import obtain_credentials
+from maascli.command import Command
+from maascli.config import ProfileConfig
+from maascli.utils import (
+ ensure_trailing_slash,
+ parse_docstring,
+ safe_name,
+ )
+
+
+class cmd_login(Command):
+ """Log-in to a remote API, storing its description and credentials.
+
+ If credentials are not provided on the command-line, they will be prompted
+ for interactively.
+ """
+
+ def __init__(self, parser):
+ super(cmd_login, self).__init__(parser)
+ parser.add_argument(
+ "profile_name", metavar="profile-name", help=(
+ "The name with which you will later refer to this remote "
+ "server and credentials within this tool."
+ ))
+ parser.add_argument(
+ "url", help=(
+ "The URL of the remote API, e.g. "
+ "http://example.com/MAAS/api/1.0/"))
+ parser.add_argument(
+ "credentials", nargs="?", default=None, help=(
+ "The credentials, also known as the API key, for the "
+ "remote MAAS server. These can be found in the user "
+ "preferences page in the web UI; they take the form of "
+ "a long random-looking string composed of three parts, "
+ "separated by colons."
+ ))
+ parser.set_defaults(credentials=None)
+
+ def __call__(self, options):
+ # Try and obtain credentials interactively if they're not given, or
+ # read them from stdin if they're specified as "-".
+ credentials = obtain_credentials(options.credentials)
+ # Normalise the remote service's URL.
+ url = ensure_trailing_slash(options.url)
+ # Get description of remote API.
+ description = fetch_api_description(url)
+ # Save the config.
+ profile_name = options.profile_name
+ with ProfileConfig.open() as config:
+ config[profile_name] = {
+ "credentials": credentials,
+ "description": description,
+ "name": profile_name,
+ "url": url,
+ }
+
+
+class cmd_refresh(Command):
+ """Refresh the API descriptions of all profiles."""
+
+ def __call__(self, options):
+ with ProfileConfig.open() as config:
+ for profile_name in config:
+ profile = config[profile_name]
+ url = profile["url"]
+ profile["description"] = fetch_api_description(url)
+ config[profile_name] = profile
+
+
+class cmd_logout(Command):
+ """Log-out of a remote API, purging any stored credentials."""
+
+ def __init__(self, parser):
+ super(cmd_logout, self).__init__(parser)
+ parser.add_argument(
+ "profile_name", metavar="profile-name", help=(
+ "The name with which a remote server and its credentials "
+ "are referred to within this tool."
+ ))
+
+ def __call__(self, options):
+ with ProfileConfig.open() as config:
+ del config[options.profile_name]
+
+
+class cmd_list(Command):
+ """List remote APIs that have been logged-in to."""
+
+ def __call__(self, options):
+ with ProfileConfig.open() as config:
+ for profile_name in config:
+ profile = config[profile_name]
+ url = profile["url"]
+ creds = profile["credentials"]
+ if creds is None:
+ print(profile_name, url)
+ else:
+ creds = convert_tuple_to_string(creds)
+ print(profile_name, url, creds)
+
+
+commands = {
+ 'login': cmd_login,
+ 'logout': cmd_logout,
+ 'list': cmd_list,
+ 'refresh': cmd_refresh,
+}
+
+
+def register_cli_commands(parser):
+ """Register the CLI's meta-subcommands on `parser`."""
+ for name, command in commands.items():
+ help_title, help_body = parse_docstring(command)
+ command_parser = parser.subparsers.add_parser(
+ safe_name(name), help=help_title, description=help_body)
+ command_parser.set_defaults(execute=command(command_parser))
=== modified file 'src/maascli/tests/test_api.py'
--- src/maascli/tests/test_api.py 2012-10-08 05:24:57 +0000
+++ src/maascli/tests/test_api.py 2012-10-08 06:34:23 +0000
@@ -80,23 +80,6 @@
self.assertIsNotNone(parser.subparsers.choices[profile])
-class TestRegisterCLICommands(TestCase):
- """Tests for `register_cli_commands`."""
-
- def test_registers_subparsers(self):
- parser = ArgumentParser()
- self.assertIsNone(parser._subparsers)
- api.register_cli_commands(parser)
- self.assertIsNotNone(parser._subparsers)
-
- def test_subparsers_have_appropriate_execute_defaults(self):
- parser = ArgumentParser()
- api.register_cli_commands(parser)
- self.assertIsInstance(
- parser.subparsers.choices['login'].get_default('execute'),
- api.cmd_login)
-
-
class TestFunctions(TestCase):
"""Test for miscellaneous functions in `maascli.api`."""
=== added file 'src/maascli/tests/test_cli.py'
--- src/maascli/tests/test_cli.py 1970-01-01 00:00:00 +0000
+++ src/maascli/tests/test_cli.py 2012-10-08 06:34:23 +0000
@@ -0,0 +1,36 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for `maascli.cli`."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = []
+
+from maascli import (
+ ArgumentParser,
+ cli,
+ )
+from maastesting.testcase import TestCase
+
+
+class TestRegisterCLICommands(TestCase):
+ """Tests for `register_cli_commands`."""
+
+ def test_registers_subparsers(self):
+ parser = ArgumentParser()
+ self.assertIsNone(parser._subparsers)
+ cli.register_cli_commands(parser)
+ self.assertIsNotNone(parser._subparsers)
+
+ def test_subparsers_have_appropriate_execute_defaults(self):
+ parser = ArgumentParser()
+ cli.register_cli_commands(parser)
+ self.assertIsInstance(
+ parser.subparsers.choices['login'].get_default('execute'),
+ cli.cmd_login)
Follow ups